Refactor http
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 9 Feb 2017 15:44:30 +0000 (16:44 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 9 Feb 2017 15:44:30 +0000 (16:44 +0100)
32 files changed:
demo/init/node/.gitignore
org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java
org.argeo.cms/src/org/argeo/cms/auth/CmsSession.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/CmsSessionId.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/HttpSessionId.java [deleted file]
org.argeo.cms/src/org/argeo/cms/auth/HttpSessionLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/WebCmsSession.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/HttpFilter.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/HttpUtils.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/NodeHttp.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/OpenInViewSessionProvider.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/RobotServlet.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/WebCmsSessionImpl.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/protectedHandlers.xml [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/http/webdav-config.xml [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsFsProvider.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/HttpFilter.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitType.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/WebCmsSessionImpl.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/protectedHandlers.xml [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/kernel/webdav-config.xml [deleted file]

index 288267a143a43f7d058eeba543087a13f0754d06..07b78c238a7de954bf7cfc11bee0f85dff335b6b 100644 (file)
@@ -1 +1,2 @@
 /krb5.keytab
+/krb5.keytab.old
index 2432d9c0a45b5a1947e8b0d2d38deb184d07140e..6dae68d1d9a4095d8a5ff161823c8084eec27b53 100644 (file)
@@ -3,6 +3,7 @@ package org.argeo.cms.auth;
 import java.security.Principal;
 import java.util.Collection;
 import java.util.Set;
+import java.util.UUID;
 
 import javax.naming.InvalidNameException;
 import javax.naming.ldap.LdapName;
@@ -17,8 +18,9 @@ import org.apache.commons.logging.LogFactory;
 //import org.apache.jackrabbit.core.security.SecurityConstants;
 //import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
 import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.auth.CmsSessionImpl;
 import org.argeo.cms.internal.auth.ImpliedByPrincipal;
-import org.argeo.cms.internal.kernel.WebCmsSessionImpl;
+import org.argeo.cms.internal.http.WebCmsSessionImpl;
 import org.argeo.node.security.AnonymousPrincipal;
 import org.argeo.node.security.DataAdminPrincipal;
 import org.argeo.node.security.NodeSecurityUtils;
@@ -121,78 +123,92 @@ class CmsAuthUtils {
 
        static void registerSessionAuthorization(BundleContext bc, HttpServletRequest request, Subject subject,
                        Authorization authorization) {
-               String httpSessId = request.getSession().getId();
+               HttpSession httpSession = request.getSession();
+               String httpSessId = httpSession.getId();
                if (authorization.getName() != null) {
                        request.setAttribute(HttpContext.REMOTE_USER, authorization.getName());
                        request.setAttribute(HttpContext.AUTHORIZATION, authorization);
 
-                       HttpSession httpSession = request.getSession();
-                       if (httpSession.getAttribute(HttpContext.AUTHORIZATION) == null) {
-
-                               Collection<ServiceReference<WebCmsSession>> sr;
-                               try {
-                                       sr = bc.getServiceReferences(WebCmsSession.class,
-                                                       "(" + WebCmsSession.CMS_SESSION_ID + "=" + httpSessId + ")");
-                               } catch (InvalidSyntaxException e) {
-                                       throw new CmsException("Cannot get CMS session for id " + httpSessId, e);
-                               }
-                               ServiceReference<WebCmsSession> cmsSessionRef;
-                               if (sr.size() == 1) {
-                                       cmsSessionRef = sr.iterator().next();
-                               } else if (sr.size() == 0) {
-                                       WebCmsSessionImpl cmsSessionImpl = new WebCmsSessionImpl(httpSessId, authorization);
-                                       cmsSessionRef = cmsSessionImpl.getServiceRegistration().getReference();
-                                       if (log.isDebugEnabled())
-                                               log.debug("Initialized " + cmsSessionImpl + " for " + authorization.getName());
-                               } else
-                                       throw new CmsException(sr.size() + " CMS sessions registered for " + httpSessId);
-
-                               WebCmsSessionImpl cmsSession = (WebCmsSessionImpl) bc.getService(cmsSessionRef);
-                               cmsSession.addHttpSession(request);
-                               if (log.isTraceEnabled())
-                                       log.trace("Added " + request.getServletPath() + " to " + cmsSession + " (" + request.getRequestURI()
-                                                       + ")");
-                               // httpSession.setAttribute(HttpContext.REMOTE_USER,
-                               // authorization.getName());
-                               // httpSession.setAttribute(HttpContext.AUTHORIZATION,
-                               // authorization);
+                       CmsSession cmsSession = CmsSessionImpl.getByLocalId(httpSessId);
+                       if (cmsSession == null)
+                               cmsSession = new WebCmsSessionImpl(subject, authorization, httpSessId);
+                       request.setAttribute(CmsSession.class.getName(), cmsSession);
+                       // else
+                       // throw new CmsException("Already a CMS session registered for
+                       // "+httpSessId);
+
+                       // if (httpSession.getAttribute(HttpContext.AUTHORIZATION) == null)
+                       // {
+
+                       // Collection<ServiceReference<CmsSession>> sr;
+                       // try {
+                       // sr = bc.getServiceReferences(CmsSession.class,
+                       // "(" + CmsSession.SESSION_LOCAL_ID + "=" + httpSessId + ")");
+                       // } catch (InvalidSyntaxException e) {
+                       // throw new CmsException("Cannot get CMS session for id " +
+                       // httpSessId, e);
+                       // }
+                       // ServiceReference<CmsSession> cmsSessionRef;
+                       // if (sr.size() == 1) {
+                       // cmsSessionRef = sr.iterator().next();
+                       // } else if (sr.size() == 0) {
+                       // WebCmsSessionImpl cmsSessionImpl = new WebCmsSessionImpl(subject,
+                       // authorization, httpSessId);
+                       // cmsSessionRef =
+                       // cmsSessionImpl.getServiceRegistration().getReference();
+                       // if (log.isDebugEnabled())
+                       // log.debug("Initialized " + cmsSessionImpl + " for " +
+                       // authorization.getName());
+                       // } else
+                       // throw new CmsException(sr.size() + " CMS sessions registered for
+                       // " + httpSessId);
+                       //
+                       // cmsSession = (CmsSession) bc.getService(cmsSessionRef);
+                       // cmsSession.addHttpSession(request);
+                       // if (log.isTraceEnabled())
+                       // log.trace("Added " + request.getServletPath() + " to " +
+                       // cmsSession + " (" + request.getRequestURI()
+                       // + ")");
+                       // httpSession.setAttribute(HttpContext.REMOTE_USER,
+                       // authorization.getName());
+                       // httpSession.setAttribute(HttpContext.AUTHORIZATION,
+                       // authorization);
+                       CmsSessionId nodeSessionId = new CmsSessionId(cmsSession.getUuid());
+                       if (subject.getPrivateCredentials(CmsSessionId.class).size() == 0)
+                               subject.getPrivateCredentials().add(nodeSessionId);
+                       else {
+                               UUID storedSessionId = subject.getPrivateCredentials(CmsSessionId.class).iterator().next().getUuid();
+                               // if (storedSessionId.equals(httpSessionId.getValue()))
+                               throw new CmsException(
+                                               "Subject already logged with session " + storedSessionId + " (not " + nodeSessionId + ")");
                        }
                }
-               HttpSessionId httpSessionId = new HttpSessionId(httpSessId);
-               if (subject.getPrivateCredentials(HttpSessionId.class).size() == 0)
-                       subject.getPrivateCredentials().add(httpSessionId);
-               else {
-                       String storedSessionId = subject.getPrivateCredentials(HttpSessionId.class).iterator().next().getValue();
-                       // if (storedSessionId.equals(httpSessionId.getValue()))
-                       throw new CmsException(
-                                       "Subject already logged with session " + storedSessionId + " (not " + httpSessionId + ")");
-               }
+               // }
        }
 
        static boolean logoutSession(BundleContext bc, Subject subject) {
-               String httpSessionId;
-               if (subject.getPrivateCredentials(HttpSessionId.class).size() == 1)
-                       httpSessionId = subject.getPrivateCredentials(HttpSessionId.class).iterator().next().getValue();
+               UUID nodeSessionId;
+               if (subject.getPrivateCredentials(CmsSessionId.class).size() == 1)
+                       nodeSessionId = subject.getPrivateCredentials(CmsSessionId.class).iterator().next().getUuid();
                else
                        return false;
-               Collection<ServiceReference<WebCmsSession>> srs;
+               Collection<ServiceReference<CmsSession>> srs;
                try {
-                       srs = bc.getServiceReferences(WebCmsSession.class,
-                                       "(" + WebCmsSession.CMS_SESSION_ID + "=" + httpSessionId + ")");
+                       srs = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + nodeSessionId + ")");
                } catch (InvalidSyntaxException e) {
-                       throw new CmsException("Cannot retrieve CMS session #" + httpSessionId, e);
+                       throw new CmsException("Cannot retrieve CMS session #" + nodeSessionId, e);
                }
 
                if (srs.size() == 0) {
                        if (log.isTraceEnabled())
-                               log.warn("No CMS web session found for http session " + httpSessionId);
+                               log.warn("No CMS web session found for http session " + nodeSessionId);
                        return false;
                } else if (srs.size() > 1)
-                       throw new CmsException(srs.size() + " CMS web sessions found for http session " + httpSessionId);
+                       throw new CmsException(srs.size() + " CMS web sessions found for http session " + nodeSessionId);
 
                WebCmsSessionImpl cmsSession = (WebCmsSessionImpl) bc.getService(srs.iterator().next());
                cmsSession.cleanUp();
-               subject.getPrivateCredentials().removeAll(subject.getPrivateCredentials(HttpSessionId.class));
+               subject.getPrivateCredentials().removeAll(subject.getPrivateCredentials(CmsSessionId.class));
                if (log.isDebugEnabled())
                        log.debug("Cleaned up " + cmsSession);
                return true;
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsSession.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsSession.java
new file mode 100644 (file)
index 0000000..14d6d71
--- /dev/null
@@ -0,0 +1,34 @@
+package org.argeo.cms.auth;
+
+import java.util.UUID;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.naming.ldap.LdapName;
+
+import org.argeo.naming.LdapAttrs;
+import org.osgi.service.useradmin.Authorization;
+
+public interface CmsSession {
+       public final static String USER_DN = LdapAttrs.DN;
+       public final static String SESSION_UUID = LdapAttrs.entryUUID.name();
+       public final static String SESSION_LOCAL_ID = LdapAttrs.uniqueIdentifier.name();
+
+       // public String getId();
+
+       public UUID getUuid();
+
+       public LdapName getUserDn();
+
+       public String getLocalId();
+
+       public Authorization getAuthorization();
+
+       public Session getDataSession(String cn, String workspace, Repository repository);
+
+       public void releaseDataSession(String cn, Session session);
+
+       // public void addHttpSession(HttpServletRequest request);
+
+       // public void cleanUp();
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsSessionId.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsSessionId.java
new file mode 100644 (file)
index 0000000..8753289
--- /dev/null
@@ -0,0 +1,35 @@
+package org.argeo.cms.auth;
+
+import java.util.UUID;
+
+import org.argeo.cms.CmsException;
+
+public class CmsSessionId {
+       private final UUID uuid;
+
+       public CmsSessionId(UUID value) {
+               if (value == null)
+                       throw new CmsException("value cannot be null");
+               this.uuid = value;
+       }
+
+       public UUID getUuid() {
+               return uuid;
+       }
+
+       @Override
+       public int hashCode() {
+               return uuid.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               return obj instanceof CmsSessionId && ((CmsSessionId) obj).getUuid().equals(uuid);
+       }
+
+       @Override
+       public String toString() {
+               return "Node Session " + uuid;
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionId.java b/org.argeo.cms/src/org/argeo/cms/auth/HttpSessionId.java
deleted file mode 100644 (file)
index 9f972de..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.argeo.cms.auth;
-
-import org.argeo.cms.CmsException;
-
-public class HttpSessionId {
-       private final String value;
-
-       public HttpSessionId(String value) {
-               if (value == null)
-                       throw new CmsException("value cannot be null");
-               this.value = value;
-       }
-
-       public String getValue() {
-               return value;
-       }
-
-       @Override
-       public int hashCode() {
-               return value.hashCode();
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               return obj instanceof HttpSessionId && ((HttpSessionId) obj).getValue().equals(value);
-       }
-
-       @Override
-       public String toString() {
-               return "HttpSessionId #" + value;
-       }
-
-}
index 382d8fe4add66aba512eb1305485d1efeaf512ed..be8a693baa2c9a6da376fc11c929e2defa1b2995 100644 (file)
@@ -72,15 +72,15 @@ public class HttpSessionLoginModule implements LoginModule {
                        // authorization = (Authorization)
                        // request.getSession().getAttribute(HttpContext.AUTHORIZATION);
                        // if (authorization == null) {
-                       Collection<ServiceReference<WebCmsSession>> sr;
+                       Collection<ServiceReference<CmsSession>> sr;
                        try {
-                               sr = bc.getServiceReferences(WebCmsSession.class,
-                                               "(" + WebCmsSession.CMS_SESSION_ID + "=" + httpSessionId + ")");
+                               sr = bc.getServiceReferences(CmsSession.class,
+                                               "(" + CmsSession.SESSION_LOCAL_ID + "=" + httpSessionId + ")");
                        } catch (InvalidSyntaxException e) {
                                throw new CmsException("Cannot get CMS session for id " + httpSessionId, e);
                        }
                        if (sr.size() == 1) {
-                               WebCmsSession cmsSession = bc.getService(sr.iterator().next());
+                               CmsSession cmsSession = bc.getService(sr.iterator().next());
                                authorization = cmsSession.getAuthorization();
                                if (log.isTraceEnabled())
                                        log.trace("Retrieved authorization from " + cmsSession);
index f5883a54f1035fc10608f7a5152380affa486c7a..b6ab04b213896db4a908112e7e7a2659016c366b 100644 (file)
@@ -71,11 +71,12 @@ public class UserAdminLoginModule implements LoginModule {
 
                final String username;
                final char[] password;
-               if (sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME)
+               if (callbackHandler == null && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_NAME)
                                && sharedState.containsKey(CmsAuthUtils.SHARED_STATE_PWD)) {
                        username = (String) sharedState.get(CmsAuthUtils.SHARED_STATE_NAME);
                        password = (char[]) sharedState.get(CmsAuthUtils.SHARED_STATE_PWD);
                        // TODO locale?
+                       // NB: raw user name is used
                        AuthenticatingUser authenticatingUser = new AuthenticatingUser(username, password);
                        authorization = userAdmin.getAuthorization(authenticatingUser);
                } else {
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/WebCmsSession.java b/org.argeo.cms/src/org/argeo/cms/auth/WebCmsSession.java
deleted file mode 100644 (file)
index 3e2eb24..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.argeo.cms.auth;
-
-import org.osgi.service.useradmin.Authorization;
-
-public interface WebCmsSession {
-       public final static String CMS_DN = "cms.dn";
-       public final static String CMS_SESSION_ID = "cms.sessionId";
-
-//     public String getId();
-
-       public Authorization getAuthorization();
-
-//     public void addHttpSession(HttpServletRequest request);
-
-//     public void cleanUp();
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java
new file mode 100644 (file)
index 0000000..71049fa
--- /dev/null
@@ -0,0 +1,208 @@
+package org.argeo.cms.internal.auth;
+
+import java.security.PrivilegedExceptionAction;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.security.auth.Subject;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.CmsSession;
+import org.argeo.jcr.JcrUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.useradmin.Authorization;
+
+public class CmsSessionImpl implements CmsSession {
+       private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionImpl.class).getBundleContext();
+       private final static Log log = LogFactory.getLog(CmsSessionImpl.class);
+
+       private final Subject initialSubject;
+       private final UUID uuid;
+       private final String localSessionId;
+       private final Authorization authorization;
+       private final LdapName userDn;
+
+       private ServiceRegistration<CmsSession> serviceRegistration;
+
+       private Map<String, Session> dataSessions = new HashMap<>();
+       private Set<String> dataSessionsInUse = new HashSet<>();
+       private LinkedHashSet<Session> additionalDataSessions = new LinkedHashSet<>();
+
+       public CmsSessionImpl(Subject initialSubject, Authorization authorization, String localSessionId) {
+               this.initialSubject = initialSubject;
+               this.localSessionId = localSessionId;
+               this.authorization = authorization;
+               try {
+                       this.userDn = new LdapName(authorization.getName());
+               } catch (InvalidNameException e) {
+                       throw new CmsException("Invalid user name " + authorization.getName(), e);
+               }
+               this.uuid = UUID.randomUUID();
+               // register as service
+               Hashtable<String, String> props = new Hashtable<>();
+               props.put(CmsSession.USER_DN, authorization.getName());
+               props.put(CmsSession.SESSION_UUID, uuid.toString());
+               props.put(CmsSession.SESSION_LOCAL_ID, localSessionId);
+               serviceRegistration = bc.registerService(CmsSession.class, this, props);
+       }
+
+       public synchronized void cleanUp() {
+               serviceRegistration.unregister();
+
+               // TODO check data session in use ?
+               for (String path : dataSessions.keySet())
+                       JcrUtils.logoutQuietly(dataSessions.get(path));
+               for (Session session : additionalDataSessions)
+                       JcrUtils.logoutQuietly(session);
+               notifyAll();
+       }
+
+       @Override
+       public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
+               String path = cn + '/' + workspace;
+               if (dataSessionsInUse.contains(path)) {
+                       try {
+                               wait(1000);
+                               if (dataSessionsInUse.contains(path)) {
+                                       Session session = login(repository, workspace);
+                                       additionalDataSessions.add(session);
+                                       if (log.isTraceEnabled())
+                                               log.trace("Additional data session " + path + " for " + userDn);
+                                       return session;
+                               }
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+               }
+
+               Session session = null;
+               if (dataSessions.containsKey(path)) {
+                       session = dataSessions.get(path);
+               } else {
+                       session = login(repository, workspace);
+                       dataSessions.put(path, session);
+                       if (log.isTraceEnabled())
+                               log.trace("New data session " + path + " for " + userDn);
+               }
+               dataSessionsInUse.add(path);
+               return session;
+       }
+
+       private Session login(Repository repository, String workspace) {
+               try {
+                       return Subject.doAs(initialSubject, new PrivilegedExceptionAction<Session>() {
+                               @Override
+                               public Session run() throws Exception {
+                                       return repository.login(workspace);
+                               }
+                       });
+               } catch (Exception e) {
+                       throw new CmsException("Cannot log in " + userDn + " to JCR", e);
+               }
+       }
+
+       @Override
+       public synchronized void releaseDataSession(String cn, Session session) {
+               if (additionalDataSessions.contains(session)) {
+                       JcrUtils.logoutQuietly(session);
+                       additionalDataSessions.remove(session);
+                       return;
+               }
+               String path = cn + '/' + session.getWorkspace().getName();
+               if (!dataSessionsInUse.contains(path))
+                       log.warn("Data session " + path + " was not in use for " + userDn);
+               dataSessionsInUse.remove(path);
+               Session registeredSession = dataSessions.get(path);
+               if (session != registeredSession)
+                       log.warn("Data session " + path + " not consistent for " + userDn);
+               notifyAll();
+       }
+
+       @Override
+       public Authorization getAuthorization() {
+               return authorization;
+       }
+
+       @Override
+       public UUID getUuid() {
+               return uuid;
+       }
+
+       public Subject getInitialSubject() {
+               return initialSubject;
+       }
+
+       public String getLocalSessionId() {
+               return localSessionId;
+       }
+
+       public ServiceRegistration<CmsSession> getServiceRegistration() {
+               return serviceRegistration;
+       }
+
+       @Override
+       public LdapName getUserDn() {
+               return userDn;
+       }
+
+       @Override
+       public String getLocalId() {
+               return localSessionId;
+       }
+
+       public String toString() {
+               return "CMS Session #" + localSessionId;
+       }
+
+       public static CmsSession getByLocalId(String localId) {
+               Collection<ServiceReference<CmsSession>> sr;
+               try {
+                       sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_LOCAL_ID + "=" + localId + ")");
+               } catch (InvalidSyntaxException e) {
+                       throw new CmsException("Cannot get CMS session for id " + localId, e);
+               }
+               ServiceReference<CmsSession> cmsSessionRef;
+               if (sr.size() == 1) {
+                       cmsSessionRef = sr.iterator().next();
+                       return bc.getService(cmsSessionRef);
+               } else if (sr.size() == 0) {
+                       return null;
+               } else
+                       throw new CmsException(sr.size() + " CMS sessions registered for " + localId);
+
+       }
+
+       public static CmsSession getByUuid(String uuid) {
+               Collection<ServiceReference<CmsSession>> sr;
+               try {
+                       sr = bc.getServiceReferences(CmsSession.class, "(" + CmsSession.SESSION_UUID + "=" + uuid + ")");
+               } catch (InvalidSyntaxException e) {
+                       throw new CmsException("Cannot get CMS session for uuid " + uuid, e);
+               }
+               ServiceReference<CmsSession> cmsSessionRef;
+               if (sr.size() == 1) {
+                       cmsSessionRef = sr.iterator().next();
+                       return bc.getService(cmsSessionRef);
+               } else if (sr.size() == 0) {
+                       return null;
+               } else
+                       throw new CmsException(sr.size() + " CMS sessions registered for " + uuid);
+
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java
new file mode 100644 (file)
index 0000000..3755201
--- /dev/null
@@ -0,0 +1,51 @@
+package org.argeo.cms.internal.http;
+
+import java.io.Serializable;
+import java.util.LinkedHashMap;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.cms.auth.CmsSession;
+
+/**
+ * Implements an open session in view patter: a new JCR session is created for
+ * each request
+ */
+class CmsSessionProvider implements SessionProvider, Serializable {
+       private static final long serialVersionUID = -1358136599534938466L;
+
+       private final static Log log = LogFactory.getLog(CmsSessionProvider.class);
+
+       private final String alias;
+
+       private LinkedHashMap<Session, CmsSession> cmsSessions = new LinkedHashMap<>();
+
+       public CmsSessionProvider(String alias) {
+               this.alias = alias;
+       }
+
+       public Session getSession(HttpServletRequest request, Repository rep, String workspace)
+                       throws javax.jcr.LoginException, ServletException, RepositoryException {
+
+               CmsSession cmsSession = WebCmsSessionImpl.getCmsSession(request);
+               Session session = cmsSession.getDataSession(alias, workspace, rep);
+               cmsSessions.put(session, cmsSession);
+               return session;
+       }
+
+       public void releaseSession(Session session) {
+               if (cmsSessions.containsKey(session)) {
+                       CmsSession cmsSession = cmsSessions.get(session);
+                       cmsSession.releaseDataSession(alias, session);
+               } else {
+                       log.warn("No CMS session for JCR session " + session);
+               }
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java b/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java
new file mode 100644 (file)
index 0000000..581d6cb
--- /dev/null
@@ -0,0 +1,188 @@
+package org.argeo.cms.internal.http;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.StringTokenizer;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.HttpRequestCallback;
+import org.argeo.cms.auth.HttpRequestCallbackHandler;
+import org.argeo.node.NodeConstants;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.http.HttpContext;
+
+class DataHttpContext implements HttpContext {
+       private final static Log log = LogFactory.getLog(DataHttpContext.class);
+
+       private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+       // FIXME Make it more unique
+       private String httpAuthRealm = "Argeo";
+
+       @Override
+       public boolean handleSecurity(final HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+               if (log.isTraceEnabled())
+                       HttpUtils.logRequestHeaders(log, request);
+               LoginContext lc;
+               try {
+                       lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, new HttpRequestCallbackHandler(request, response));
+                       lc.login();
+                       // return true;
+               } catch (LoginException e) {
+                       CallbackHandler token = extractHttpAuth(request, response);
+                       if (token != null) {
+                               try {
+                                       lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, token);
+                                       lc.login();
+                               } catch (LoginException e1) {
+                                       throw new CmsException("Could not login", e1);
+                               }
+                       } else {
+                               lc = processUnauthorized(request, response);
+                               if (lc == null)
+                                       return false;
+                       }
+               }
+               request.setAttribute(NodeConstants.LOGIN_CONTEXT_USER, lc);
+               return true;
+       }
+
+       @Override
+       public URL getResource(String name) {
+               return bc.getBundle().getResource(name);
+       }
+
+       @Override
+       public String getMimeType(String name) {
+               return null;
+       }
+
+       protected LoginContext processUnauthorized(HttpServletRequest request, HttpServletResponse response) {
+               // anonymous
+               try {
+                       LoginContext lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER);
+                       lc.login();
+                       return lc;
+               } catch (LoginException e1) {
+                       if (log.isDebugEnabled())
+                               log.error("Cannot log in as anonymous", e1);
+                       return null;
+               }
+       }
+
+       protected CallbackHandler extractHttpAuth(final HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
+               String authHeader = httpRequest.getHeader(HttpUtils.HEADER_AUTHORIZATION);
+               if (authHeader != null) {
+                       StringTokenizer st = new StringTokenizer(authHeader);
+                       if (st.hasMoreTokens()) {
+                               String basic = st.nextToken();
+                               if (basic.equalsIgnoreCase("Basic")) {
+                                       try {
+                                               // TODO manipulate char[]
+                                               String credentials = new String(Base64.decodeBase64(st.nextToken()), "UTF-8");
+                                               // log.debug("Credentials: " + credentials);
+                                               int p = credentials.indexOf(":");
+                                               if (p != -1) {
+                                                       final String login = credentials.substring(0, p).trim();
+                                                       final char[] password = credentials.substring(p + 1).trim().toCharArray();
+                                                       return new CallbackHandler() {
+                                                               public void handle(Callback[] callbacks) {
+                                                                       for (Callback cb : callbacks) {
+                                                                               if (cb instanceof NameCallback)
+                                                                                       ((NameCallback) cb).setName(login);
+                                                                               else if (cb instanceof PasswordCallback)
+                                                                                       ((PasswordCallback) cb).setPassword(password);
+                                                                               else if (cb instanceof HttpRequestCallback) {
+                                                                                       ((HttpRequestCallback) cb).setRequest(httpRequest);
+                                                                                       ((HttpRequestCallback) cb).setResponse(httpResponse);
+                                                                               }
+                                                                       }
+                                                               }
+                                                       };
+                                               } else {
+                                                       throw new CmsException("Invalid authentication token");
+                                               }
+                                       } catch (Exception e) {
+                                               throw new CmsException("Couldn't retrieve authentication", e);
+                                       }
+                               } else if (basic.equalsIgnoreCase("Negotiate")) {
+                                       // FIXME generalise
+                                       String _targetName = "HTTP/mostar.desktop.argeo.pro";
+                                       String spnegoToken = st.nextToken();
+                                       byte[] authToken = Base64.decodeBase64(spnegoToken);
+                                       GSSManager manager = GSSManager.getInstance();
+                                       try {
+                                               Oid krb5Oid = new Oid("1.3.6.1.5.5.2"); // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
+                                               GSSName gssName = manager.createName(_targetName, null);
+                                               GSSCredential serverCreds = manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME,
+                                                               krb5Oid, GSSCredential.ACCEPT_ONLY);
+                                               GSSContext gContext = manager.createContext(serverCreds);
+
+                                               if (gContext == null) {
+                                                       log.debug("SpnegoUserRealm: failed to establish GSSContext");
+                                               } else {
+                                                       while (!gContext.isEstablished()) {
+                                                               byte[] outToken = gContext.acceptSecContext(authToken, 0, authToken.length);
+                                                               String outTokenStr = Base64.encodeBase64String(outToken);
+                                                               httpResponse.setHeader("WWW-Authenticate", "Negotiate " + outTokenStr);
+                                                       }
+                                                       if (gContext.isEstablished()) {
+                                                               String clientName = gContext.getSrcName().toString();
+                                                               String role = clientName.substring(clientName.indexOf('@') + 1);
+
+                                                               log.debug("SpnegoUserRealm: established a security context");
+                                                               log.debug("Client Principal is: " + gContext.getSrcName());
+                                                               log.debug("Server Principal is: " + gContext.getTargName());
+                                                               log.debug("Client Default Role: " + role);
+
+                                                               // TODO log in
+                                                       }
+                                               }
+
+                                       } catch (GSSException gsse) {
+                                               log.warn(gsse, gsse);
+                                       }
+
+                               }
+                       }
+               }
+               return null;
+       }
+
+       protected void askForWwwAuth(HttpServletRequest request, HttpServletResponse response) {
+               response.setStatus(401);
+               response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic realm=\"" + httpAuthRealm + "\"");
+
+               // SPNEGO
+               // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate");
+               // 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");
+
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/HttpFilter.java b/org.argeo.cms/src/org/argeo/cms/internal/http/HttpFilter.java
new file mode 100644 (file)
index 0000000..142bccb
--- /dev/null
@@ -0,0 +1,40 @@
+package org.argeo.cms.internal.http;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/** Abstract base class for http filters. */
+abstract class HttpFilter implements Filter {
+       // private final static Log log = LogFactory.getLog(HttpFilter.class);
+
+       protected abstract void doFilter(HttpSession httpSession,
+                       HttpServletRequest request, HttpServletResponse response,
+                       FilterChain filterChain) throws IOException, ServletException;
+
+       @Override
+       public void doFilter(ServletRequest servletRequest,
+                       ServletResponse servletResponse, FilterChain filterChain)
+                       throws IOException, ServletException {
+               HttpServletRequest request = (HttpServletRequest) servletRequest;
+               doFilter(request.getSession(), request,
+                               (HttpServletResponse) servletResponse, filterChain);
+       }
+
+       @Override
+       public void destroy() {
+       }
+
+       @Override
+       public void init(FilterConfig arg0) throws ServletException {
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/HttpUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/http/HttpUtils.java
new file mode 100644 (file)
index 0000000..efa2d66
--- /dev/null
@@ -0,0 +1,64 @@
+package org.argeo.cms.internal.http;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+
+class HttpUtils {
+       final static String HEADER_AUTHORIZATION = "Authorization";
+       final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
+
+       final static String DEFAULT_PROTECTED_HANDLERS = "/org/argeo/cms/internal/http/protectedHandlers.xml";
+       final static String WEBDAV_CONFIG = "/org/argeo/cms/internal/http/webdav-config.xml";
+
+       static boolean isBrowser(String userAgent) {
+               return userAgent.contains("webkit") || userAgent.contains("gecko") || userAgent.contains("firefox")
+                               || userAgent.contains("msie") || userAgent.contains("chrome") || userAgent.contains("chromium")
+                               || userAgent.contains("opera") || userAgent.contains("browser");
+       }
+
+       static void logRequestHeaders(Log log, HttpServletRequest request) {
+               if (!log.isDebugEnabled())
+                       return;
+               for (Enumeration<String> headerNames = request.getHeaderNames(); headerNames.hasMoreElements();) {
+                       String headerName = headerNames.nextElement();
+                       Object headerValue = request.getHeader(headerName);
+                       log.debug(headerName + ": " + headerValue);
+               }
+               log.debug(request.getRequestURI() + "\n");
+       }
+
+       static void logRequest(Log log,HttpServletRequest request) {
+               log.debug("contextPath=" + request.getContextPath());
+               log.debug("servletPath=" + request.getServletPath());
+               log.debug("requestURI=" + request.getRequestURI());
+               log.debug("queryString=" + request.getQueryString());
+               StringBuilder buf = new StringBuilder();
+               // headers
+               Enumeration<String> en = request.getHeaderNames();
+               while (en.hasMoreElements()) {
+                       String header = en.nextElement();
+                       Enumeration<String> values = request.getHeaders(header);
+                       while (values.hasMoreElements())
+                               buf.append("  " + header + ": " + values.nextElement());
+                       buf.append('\n');
+               }
+
+               // attributed
+               Enumeration<String> an = request.getAttributeNames();
+               while (an.hasMoreElements()) {
+                       String attr = an.nextElement();
+                       Object value = request.getAttribute(attr);
+                       buf.append("  " + attr + ": " + value);
+                       buf.append('\n');
+               }
+               log.debug("\n" + buf);
+       }
+
+
+       private HttpUtils() {
+
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java b/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java
new file mode 100644 (file)
index 0000000..3c290e1
--- /dev/null
@@ -0,0 +1,258 @@
+package org.argeo.cms.internal.http;
+
+import static javax.jcr.Property.JCR_DESCRIPTION;
+import static javax.jcr.Property.JCR_LAST_MODIFIED;
+import static javax.jcr.Property.JCR_TITLE;
+import static org.argeo.cms.CmsTypes.CMS_IMAGE;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.PrivilegedExceptionAction;
+import java.util.Calendar;
+import java.util.Collection;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.argeo.cms.CmsException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.node.NodeConstants;
+import org.argeo.node.NodeUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+
+class LinkServlet extends HttpServlet {
+       private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+       private static final long serialVersionUID = 3749990143146845708L;
+
+       @Override
+       protected void service(HttpServletRequest request, HttpServletResponse response)
+                       throws ServletException, IOException {
+               String path = request.getPathInfo();
+               String userAgent = request.getHeader("User-Agent").toLowerCase();
+               boolean isBot = false;
+               // boolean isCompatibleBrowser = false;
+               if (userAgent.contains("bot") || userAgent.contains("facebook") || userAgent.contains("twitter")) {
+                       isBot = true;
+               }
+               // else if (userAgent.contains("webkit") ||
+               // userAgent.contains("gecko") || userAgent.contains("firefox")
+               // || userAgent.contains("msie") || userAgent.contains("chrome") ||
+               // userAgent.contains("chromium")
+               // || userAgent.contains("opera") || userAgent.contains("browser"))
+               // {
+               // isCompatibleBrowser = true;
+               // }
+
+               if (isBot) {
+                       // log.warn("# BOT " + request.getHeader("User-Agent"));
+                       canonicalAnswer(request, response, path);
+                       return;
+               }
+
+               // if (isCompatibleBrowser && log.isTraceEnabled())
+               // log.trace("# BWS " + request.getHeader("User-Agent"));
+               redirectTo(response, "/#" + path);
+       }
+
+       private void redirectTo(HttpServletResponse response, String location) {
+               response.setHeader("Location", location);
+               response.setStatus(HttpServletResponse.SC_FOUND);
+       }
+
+       // private boolean canonicalAnswerNeededBy(HttpServletRequest request) {
+       // String userAgent = request.getHeader("User-Agent").toLowerCase();
+       // return userAgent.startsWith("facebookexternalhit/");
+       // }
+
+       /** For bots which don't understand RWT. */
+       private void canonicalAnswer(HttpServletRequest request, HttpServletResponse response, String path) {
+               Session session = null;
+               try {
+                       PrintWriter writer = response.getWriter();
+                       session = Subject.doAs(anonymousLogin(), new PrivilegedExceptionAction<Session>() {
+
+                               @Override
+                               public Session run() throws Exception {
+                                       Collection<ServiceReference<Repository>> srs = bc.getServiceReferences(Repository.class,
+                                                       "(" + NodeConstants.CN + "=" + NodeConstants.NODE + ")");
+                                       Repository repository = bc.getService(srs.iterator().next());
+                                       return repository.login();
+                               }
+
+                       });
+                       Node node = session.getNode(path);
+                       String title = node.hasProperty(JCR_TITLE) ? node.getProperty(JCR_TITLE).getString() : node.getName();
+                       String desc = node.hasProperty(JCR_DESCRIPTION) ? node.getProperty(JCR_DESCRIPTION).getString() : null;
+                       Calendar lastUpdate = node.hasProperty(JCR_LAST_MODIFIED) ? node.getProperty(JCR_LAST_MODIFIED).getDate()
+                                       : null;
+                       String url = getCanonicalUrl(node, request);
+                       String imgUrl = null;
+                       loop: for (NodeIterator it = node.getNodes(); it.hasNext();) {
+                               // Takes the first found cms:image
+                               Node child = it.nextNode();
+                               if (child.isNodeType(CMS_IMAGE)) {
+                                       imgUrl = getDataUrl(child, request);
+                                       break loop;
+                               }
+                       }
+                       StringBuilder buf = new StringBuilder();
+                       buf.append("<html>");
+                       buf.append("<head>");
+                       writeMeta(buf, "og:title", escapeHTML(title));
+                       writeMeta(buf, "og:type", "website");
+                       buf.append("<meta name='twitter:card' content='summary' />");
+                       buf.append("<meta name='twitter:site' content='@argeo_org' />");
+                       writeMeta(buf, "og:url", url);
+                       if (desc != null)
+                               writeMeta(buf, "og:description", escapeHTML(desc));
+                       if (imgUrl != null)
+                               writeMeta(buf, "og:image", imgUrl);
+                       if (lastUpdate != null)
+                               writeMeta(buf, "og:updated_time", Long.toString(lastUpdate.getTime().getTime()));
+                       buf.append("</head>");
+                       buf.append("<body>");
+                       buf.append("<p><b>!! This page is meant for indexing robots, not for real people," + " visit <a href='/#")
+                                       .append(path).append("'>").append(escapeHTML(title)).append("</a> instead.</b></p>");
+                       writeCanonical(buf, node);
+                       buf.append("</body>");
+                       buf.append("</html>");
+                       writer.print(buf.toString());
+
+                       response.setHeader("Content-Type", "text/html");
+                       writer.flush();
+               } catch (Exception e) {
+                       throw new CmsException("Cannot write canonical answer", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+       }
+
+       /**
+        * From http://stackoverflow.com/questions/1265282/recommended-method-for-
+        * escaping-html-in-java (+ escaping '). TODO Use
+        * org.apache.commons.lang.StringEscapeUtils
+        */
+       private String escapeHTML(String s) {
+               StringBuilder out = new StringBuilder(Math.max(16, s.length()));
+               for (int i = 0; i < s.length(); i++) {
+                       char c = s.charAt(i);
+                       if (c > 127 || c == '\'' || c == '"' || c == '<' || c == '>' || c == '&') {
+                               out.append("&#");
+                               out.append((int) c);
+                               out.append(';');
+                       } else {
+                               out.append(c);
+                       }
+               }
+               return out.toString();
+       }
+
+       private void writeMeta(StringBuilder buf, String tag, String value) {
+               buf.append("<meta property='").append(tag).append("' content='").append(value).append("'/>");
+       }
+
+       private void writeCanonical(StringBuilder buf, Node node) throws RepositoryException {
+               buf.append("<div>");
+               if (node.hasProperty(JCR_TITLE))
+                       buf.append("<p>").append(node.getProperty(JCR_TITLE).getString()).append("</p>");
+               if (node.hasProperty(JCR_DESCRIPTION))
+                       buf.append("<p>").append(node.getProperty(JCR_DESCRIPTION).getString()).append("</p>");
+               NodeIterator children = node.getNodes();
+               while (children.hasNext()) {
+                       writeCanonical(buf, children.nextNode());
+               }
+               buf.append("</div>");
+       }
+
+       // DATA
+       private StringBuilder getServerBaseUrl(HttpServletRequest request) {
+               try {
+                       URL url = new URL(request.getRequestURL().toString());
+                       StringBuilder buf = new StringBuilder();
+                       buf.append(url.getProtocol()).append("://").append(url.getHost());
+                       if (url.getPort() != -1)
+                               buf.append(':').append(url.getPort());
+                       return buf;
+               } catch (MalformedURLException e) {
+                       throw new CmsException("Cannot extract server base URL from " + request.getRequestURL(), e);
+               }
+       }
+
+       private String getDataUrl(Node node, HttpServletRequest request) throws RepositoryException {
+               try {
+                       StringBuilder buf = getServerBaseUrl(request);
+                       buf.append(NodeUtils.getDataPath(NodeConstants.NODE, node));
+                       return new URL(buf.toString()).toString();
+               } catch (MalformedURLException e) {
+                       throw new CmsException("Cannot build data URL for " + node, e);
+               }
+       }
+
+       // public static String getDataPath(Node node) throws
+       // RepositoryException {
+       // assert node != null;
+       // String userId = node.getSession().getUserID();
+       //// if (log.isTraceEnabled())
+       //// log.trace(userId + " : " + node.getPath());
+       // StringBuilder buf = new StringBuilder();
+       // boolean isAnonymous =
+       // userId.equalsIgnoreCase(NodeConstants.ROLE_ANONYMOUS);
+       // if (isAnonymous)
+       // buf.append(WEBDAV_PUBLIC);
+       // else
+       // buf.append(WEBDAV_PRIVATE);
+       // Session session = node.getSession();
+       // Repository repository = session.getRepository();
+       // String cn;
+       // if (repository.isSingleValueDescriptor(NodeConstants.CN)) {
+       // cn = repository.getDescriptor(NodeConstants.CN);
+       // } else {
+       //// log.warn("No cn defined in repository, using " +
+       // NodeConstants.NODE);
+       // cn = NodeConstants.NODE;
+       // }
+       // return
+       // buf.append('/').append(cn).append('/').append(session.getWorkspace().getName()).append(node.getPath())
+       // .toString();
+       // }
+
+       private String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
+               try {
+                       StringBuilder buf = getServerBaseUrl(request);
+                       buf.append('/').append('!').append(node.getPath());
+                       return new URL(buf.toString()).toString();
+               } catch (MalformedURLException e) {
+                       throw new CmsException("Cannot build data URL for " + node, e);
+               }
+               // return request.getRequestURL().append('!').append(node.getPath())
+               // .toString();
+       }
+
+       private Subject anonymousLogin() {
+               Subject subject = new Subject();
+               LoginContext lc;
+               try {
+                       lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject);
+                       lc.login();
+                       return subject;
+               } catch (LoginException e) {
+                       throw new CmsException("Cannot login as anonymous", e);
+               }
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/http/NodeHttp.java
new file mode 100644 (file)
index 0000000..f67c342
--- /dev/null
@@ -0,0 +1,344 @@
+package org.argeo.cms.internal.http;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
+import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.kernel.KernelConstants;
+import org.argeo.node.NodeConstants;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Intercepts and enriches http access, mainly focusing on security and
+ * transactionality.
+ */
+public class NodeHttp implements KernelConstants {
+       private final static Log log = LogFactory.getLog(NodeHttp.class);
+
+       // Filters
+       // private final RootFilter rootFilter;
+
+       // private final DoSFilter dosFilter;
+       // private final QoSFilter qosFilter;
+
+       private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+       private ServiceTracker<Repository, Repository> repositories;
+       private final ServiceTracker<HttpService, HttpService> httpServiceTracker;
+
+       public NodeHttp() {
+               // rootFilter = new RootFilter();
+               // dosFilter = new CustomDosFilter();
+               // qosFilter = new QoSFilter();
+
+               httpServiceTracker = new PrepareHttpStc();
+               httpServiceTracker.open();
+       }
+
+       // class CustomDosFilter extends DoSFilter {
+       // @Override
+       // protected String extractUserId(ServletRequest request) {
+       // HttpSession httpSession = ((HttpServletRequest) request)
+       // .getSession();
+       // if (isSessionAuthenticated(httpSession)) {
+       // String userId = ((SecurityContext) httpSession
+       // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
+       // .getAuthentication().getName();
+       // return userId;
+       // }
+       // return super.extractUserId(request);
+       //
+       // }
+       // }
+
+       public void destroy() {
+               repositories.close();
+       }
+
+       void registerRepositoryServlets(HttpService httpService, String alias, Repository repository) {
+               if (httpService == null)
+                       throw new CmsException("No HTTP service available");
+               try {
+                       registerWebdavServlet(httpService, alias, repository);
+                       registerRemotingServlet(httpService, alias, repository);
+                       if (NodeConstants.HOME.equals(alias))
+                               registerFilesServlet(httpService, alias, repository);
+                       if (log.isDebugEnabled())
+                               log.debug("Registered servlets for repository '" + alias + "'");
+               } catch (Exception e) {
+                       throw new CmsException("Could not register servlets for repository '" + alias + "'", e);
+               }
+       }
+
+       void unregisterRepositoryServlets(HttpService httpService, String alias) {
+               if (httpService == null)
+                       return;
+               try {
+                       httpService.unregister(webdavPath(alias));
+                       httpService.unregister(remotingPath(alias));
+                       httpService.unregister(filesPath(alias));
+                       if (log.isDebugEnabled())
+                               log.debug("Unregistered servlets for repository '" + alias + "'");
+               } catch (Exception e) {
+                       log.error("Could not unregister servlets for repository '" + alias + "'", e);
+               }
+       }
+
+       void registerWebdavServlet(HttpService httpService, String alias, Repository repository)
+                       throws NamespaceException, ServletException {
+               // WebdavServlet webdavServlet = new WebdavServlet(repository, new
+               // OpenInViewSessionProvider(alias));
+               WebdavServlet webdavServlet = new WebdavServlet(repository, new CmsSessionProvider(alias));
+               String path = webdavPath(alias);
+               Properties ip = new Properties();
+               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG);
+               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
+               httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext());
+       }
+
+       void registerFilesServlet(HttpService httpService, String alias, Repository repository)
+                       throws NamespaceException, ServletException {
+               WebdavServlet filesServlet = new WebdavServlet(repository, new CmsSessionProvider(alias));
+               String path = filesPath(alias);
+               Properties ip = new Properties();
+               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG);
+               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
+               httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext());
+       }
+
+       void registerRemotingServlet(HttpService httpService, String alias, Repository repository)
+                       throws NamespaceException, ServletException {
+               RemotingServlet remotingServlet = new RemotingServlet(repository, new CmsSessionProvider(alias));
+               String path = remotingPath(alias);
+               Properties ip = new Properties();
+               ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
+
+               // Looks like a bug in Jackrabbit remoting init
+               Path tmpDir;
+               try {
+                       tmpDir = Files.createTempDirectory("remoting_" + alias);
+               } catch (IOException e) {
+                       throw new CmsException("Cannot create temp directory for remoting servlet", e);
+               }
+               ip.setProperty(RemotingServlet.INIT_PARAM_HOME, tmpDir.toString());
+               ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting_" + alias);
+               ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, HttpUtils.DEFAULT_PROTECTED_HANDLERS);
+               ip.setProperty(RemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false");
+               httpService.registerServlet(path, remotingServlet, ip, new PrivateHttpContext());
+       }
+
+       private String webdavPath(String alias) {
+               return NodeConstants.PATH_DATA + "/" + alias;
+       }
+
+       private String remotingPath(String alias) {
+               return NodeConstants.PATH_JCR + "/" + alias;
+       }
+
+       private String filesPath(String alias) {
+               return NodeConstants.PATH_FILES + "/" + alias;
+       }
+
+       // private Subject subjectFromRequest(HttpServletRequest request,
+       // HttpServletResponse response) {
+       // Authorization authorization = (Authorization)
+       // request.getAttribute(HttpContext.AUTHORIZATION);
+       // if (authorization == null)
+       // throw new CmsException("Not authenticated");
+       // try {
+       // LoginContext lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
+       // new HttpRequestCallbackHandler(request, response));
+       // lc.login();
+       // return lc.getSubject();
+       // } catch (LoginException e) {
+       // throw new CmsException("Cannot login", e);
+       // }
+       // }
+
+       private class RepositoriesStc extends ServiceTracker<Repository, Repository> {
+               private final HttpService httpService;
+
+               public RepositoriesStc(HttpService httpService) {
+                       super(bc, Repository.class, null);
+                       this.httpService = httpService;
+               }
+
+               @Override
+               public Repository addingService(ServiceReference<Repository> reference) {
+                       Repository repository = bc.getService(reference);
+                       Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
+                       if (jcrRepoAlias != null) {
+                               String alias = jcrRepoAlias.toString();
+                               registerRepositoryServlets(httpService, alias, repository);
+                       }
+                       return repository;
+               }
+
+               @Override
+               public void modifiedService(ServiceReference<Repository> reference, Repository service) {
+               }
+
+               @Override
+               public void removedService(ServiceReference<Repository> reference, Repository service) {
+                       Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
+                       if (jcrRepoAlias != null) {
+                               String alias = jcrRepoAlias.toString();
+                               unregisterRepositoryServlets(httpService, alias);
+                       }
+               }
+       }
+
+       private class PrepareHttpStc extends ServiceTracker<HttpService, HttpService> {
+               // private DataHttp dataHttp;
+               // private NodeHttp nodeHttp;
+
+               public PrepareHttpStc() {
+                       super(bc, HttpService.class, null);
+               }
+
+               @Override
+               public HttpService addingService(ServiceReference<HttpService> reference) {
+                       HttpService httpService = addHttpService(reference);
+                       return httpService;
+               }
+
+               @Override
+               public void removedService(ServiceReference<HttpService> reference, HttpService service) {
+                       // if (dataHttp != null)
+                       // dataHttp.destroy();
+                       // dataHttp = null;
+                       // if (nodeHttp != null)
+                       // nodeHttp.destroy();
+                       // nodeHttp = null;
+                       // destroy();
+                       repositories.close();
+                       repositories = null;
+               }
+
+               private HttpService addHttpService(ServiceReference<HttpService> sr) {
+                       HttpService httpService = bc.getService(sr);
+                       // TODO find constants
+                       Object httpPort = sr.getProperty("http.port");
+                       Object httpsPort = sr.getProperty("https.port");
+
+                       try {
+                               httpService.registerServlet("/!", new LinkServlet(), null, null);
+                               httpService.registerServlet("/robots.txt", new RobotServlet(), null, null);
+                       } catch (Exception e) {
+                               throw new CmsException("Cannot register filters", e);
+                       }
+                       // track repositories
+                       if (repositories != null)
+                               throw new CmsException("An http service is already configured");
+                       repositories = new RepositoriesStc(httpService);
+                       repositories.open();
+                       log.info(httpPortsMsg(httpPort, httpsPort));
+                       // httpAvailable = true;
+                       // checkReadiness();
+
+                       bc.registerService(NodeHttp.class, NodeHttp.this, null);
+                       return httpService;
+               }
+
+               private String httpPortsMsg(Object httpPort, Object httpsPort) {
+                       return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : "");
+               }
+       }
+
+       private class WebdavServlet extends SimpleWebdavServlet {
+               private static final long serialVersionUID = -4687354117811443881L;
+               private final Repository repository;
+
+               public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
+                       this.repository = repository;
+                       setSessionProvider(sessionProvider);
+               }
+
+               public Repository getRepository() {
+                       return repository;
+               }
+
+               @Override
+               protected void service(final HttpServletRequest request, final HttpServletResponse response)
+                               throws ServletException, IOException {
+                       WebdavServlet.super.service(request, response);
+                       // try {
+                       // Subject subject = subjectFromRequest(request);
+                       // // TODO make it stronger, with eTags.
+                       // // if (CurrentUser.isAnonymous(subject) &&
+                       // // request.getMethod().equals("GET")) {
+                       // // response.setHeader("Cache-Control", "no-transform, public,
+                       // // max-age=300, s-maxage=900");
+                       // // }
+                       //
+                       // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
+                       // @Override
+                       // public Void run() throws Exception {
+                       // WebdavServlet.super.service(request, response);
+                       // return null;
+                       // }
+                       // });
+                       // } catch (PrivilegedActionException e) {
+                       // throw new CmsException("Cannot process webdav request",
+                       // e.getException());
+                       // }
+               }
+       }
+
+       private class RemotingServlet extends JcrRemotingServlet {
+               private static final long serialVersionUID = 4605238259548058883L;
+               private final Repository repository;
+               private final SessionProvider sessionProvider;
+
+               public RemotingServlet(Repository repository, SessionProvider sessionProvider) {
+                       this.repository = repository;
+                       this.sessionProvider = sessionProvider;
+               }
+
+               @Override
+               protected Repository getRepository() {
+                       return repository;
+               }
+
+               @Override
+               protected SessionProvider getSessionProvider() {
+                       return sessionProvider;
+               }
+
+               @Override
+               protected void service(final HttpServletRequest request, final HttpServletResponse response)
+                               throws ServletException, IOException {
+                       // try {
+                       // Subject subject = subjectFromRequest(request, response);
+                       // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
+                       // @Override
+                       // public Void run() throws Exception {
+                       RemotingServlet.super.service(request, response);
+                       // return null;
+                       // }
+                       // });
+                       // } catch (PrivilegedActionException e) {
+                       // throw new CmsException("Cannot process JCR remoting request",
+                       // e.getException());
+                       // }
+               }
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/OpenInViewSessionProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/http/OpenInViewSessionProvider.java
new file mode 100644 (file)
index 0000000..8fcb3db
--- /dev/null
@@ -0,0 +1,70 @@
+package org.argeo.cms.internal.http;
+
+import java.io.Serializable;
+import java.security.PrivilegedExceptionAction;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.cms.CmsException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.node.NodeConstants;
+
+/**
+ * Implements an open session in view patter: a new JCR session is created for
+ * each request
+ */
+class OpenInViewSessionProvider implements SessionProvider, Serializable {
+       private final static Log log = LogFactory.getLog(OpenInViewSessionProvider.class);
+
+       private static final long serialVersionUID = 2270957712453841368L;
+       private final String alias;
+
+       public OpenInViewSessionProvider(String alias) {
+               this.alias = alias;
+       }
+
+       public Session getSession(HttpServletRequest request, Repository rep, String workspace)
+                       throws javax.jcr.LoginException, ServletException, RepositoryException {
+               return login(request, rep, workspace);
+       }
+
+       protected Session login(HttpServletRequest request, Repository repository, String workspace)
+                       throws RepositoryException {
+               if (log.isTraceEnabled())
+                       log.trace("Repo " + alias + ", login to workspace " + (workspace == null ? "<default>" : workspace)
+                                       + " in web session " + request.getSession().getId());
+               LoginContext lc = (LoginContext) request.getAttribute(NodeConstants.LOGIN_CONTEXT_USER);
+               if (lc == null)
+                       throw new CmsException("No login context available");
+               try {
+                       // LoginContext lc = new
+                       // LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
+                       // new HttpRequestCallbackHandler(request));
+                       // lc.login();
+                       return Subject.doAs(lc.getSubject(), new PrivilegedExceptionAction<Session>() {
+                               @Override
+                               public Session run() throws Exception {
+                                       return repository.login(workspace);
+                               }
+                       });
+               } catch (Exception e) {
+                       throw new CmsException("Cannot log in to JCR", e);
+               }
+               // return repository.login(workspace);
+       }
+
+       public void releaseSession(Session session) {
+               JcrUtils.logoutQuietly(session);
+               if (log.isTraceEnabled())
+                       log.trace("Logged out remote JCR session " + session);
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java b/org.argeo.cms/src/org/argeo/cms/internal/http/PrivateHttpContext.java
new file mode 100644 (file)
index 0000000..e0d7132
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.cms.internal.http;
+
+import javax.security.auth.login.LoginContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+class PrivateHttpContext extends DataHttpContext {
+
+       @Override
+       protected LoginContext processUnauthorized(HttpServletRequest request, HttpServletResponse response) {
+               askForWwwAuth(request, response);
+               return null;
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/RobotServlet.java b/org.argeo.cms/src/org/argeo/cms/internal/http/RobotServlet.java
new file mode 100644 (file)
index 0000000..92d9eb7
--- /dev/null
@@ -0,0 +1,24 @@
+package org.argeo.cms.internal.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+class RobotServlet extends HttpServlet {
+       private static final long serialVersionUID = 7935661175336419089L;
+
+       @Override
+       protected void service(HttpServletRequest request, HttpServletResponse response)
+                       throws ServletException, IOException {
+               PrintWriter writer = response.getWriter();
+               writer.append("User-agent: *\n");
+               writer.append("Disallow:\n");
+               response.setHeader("Content-Type", "text/plain");
+               writer.flush();
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/WebCmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/http/WebCmsSessionImpl.java
new file mode 100644 (file)
index 0000000..139e23b
--- /dev/null
@@ -0,0 +1,22 @@
+package org.argeo.cms.internal.http;
+
+import javax.security.auth.Subject;
+import javax.servlet.http.HttpServletRequest;
+
+import org.argeo.cms.auth.CmsSession;
+import org.argeo.cms.internal.auth.CmsSessionImpl;
+import org.osgi.service.useradmin.Authorization;
+
+public class WebCmsSessionImpl extends CmsSessionImpl {
+
+       public WebCmsSessionImpl(Subject initialSubject, Authorization authorization, String httpSessionId) {
+               super(initialSubject, authorization, httpSessionId);
+       }
+
+       public static CmsSession getCmsSession(HttpServletRequest request) {
+               CmsSession cmsSession = (CmsSession) request.getAttribute(CmsSession.class.getName());
+               if (cmsSession != null)
+                       return cmsSession;
+               return CmsSessionImpl.getByLocalId(request.getSession().getId());
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/protectedHandlers.xml b/org.argeo.cms/src/org/argeo/cms/internal/http/protectedHandlers.xml
new file mode 100644 (file)
index 0000000..59f22cd
--- /dev/null
@@ -0,0 +1,5 @@
+<config>
+       <protecteditemremovehandler>
+               <class name="org.apache.jackrabbit.server.remoting.davex.AclRemoveHandler" />
+       </protecteditemremovehandler>
+</config>
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/webdav-config.xml b/org.argeo.cms/src/org/argeo/cms/internal/http/webdav-config.xml
new file mode 100644 (file)
index 0000000..da4e18b
--- /dev/null
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+  -->
+<!--
+<!DOCTYPE config [
+        <!ELEMENT config (iomanager , propertymanager, (collection | noncollection)? , filter?, mimetypeproperties?) >
+
+        <!ELEMENT iomanager (class, iohandler*) >
+        <!ELEMENT iohandler (class) >
+
+        <!ELEMENT propertymanager (class, propertyhandler*) >
+        <!ELEMENT propertyhandler (class) >
+
+        <!ELEMENT collection (nodetypes) >
+        <!ELEMENT noncollection (nodetypes) >
+
+        <!ELEMENT filter (class, namespaces?, nodetypes?) >
+
+        <!ELEMENT class >
+        <!ATTLIST class
+            name  CDATA #REQUIRED
+        >
+        <!ELEMENT namespaces (prefix | uri)* >
+        <!ELEMENT prefix (CDATA) >
+        <!ELEMENT uri (CDATA) >
+
+        <!ELEMENT nodetypes (nodetype)* >
+        <!ELEMENT nodetype (CDATA) >
+
+        <!ELEMENT mimetypeproperties (mimemapping*, defaultmimetype) >
+
+        <!ELEMENT mimemapping >
+        <!ATTLIST mimemapping
+            extension  CDATA #REQUIRED
+            mimetype  CDATA #REQUIRED
+        >
+
+        <!ELEMENT defaultmimetype (CDATA) >
+]>
+-->
+
+<config>
+    <!--
+     Defines the IOManager implementation that is responsible for passing
+     import/export request to the individual IO-handlers.
+    -->
+    <iomanager>
+        <!-- class element defines the manager to be used. The specified class
+             must implement the IOManager interface.
+             Note, that the handlers are being added and called in the order
+             they appear in the configuration.
+        -->
+        <class name="org.apache.jackrabbit.server.io.IOManagerImpl" />
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHandler" />
+        </iohandler>
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHistoryHandler" />
+        </iohandler>
+<!--         <iohandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.ZipHandler" /> -->
+<!--         </iohandler> -->
+<!--         <iohandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.XmlHandler" /> -->
+<!--         </iohandler> -->
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.DirListingExportHandler" />
+        </iohandler>
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
+        </iohandler>
+    </iomanager>
+    <!--
+     Example config for iomanager that populates its list of handlers with
+     default values. Therefore the 'iohandler' elements are omited.
+    -->
+    <!--
+    <iomanager>
+        <class name="org.apache.jackrabbit.server.io.DefaultIOManager" />
+    </iomanager>
+    -->
+    <!--
+     Defines the PropertyManager implementation that is responsible for export
+     and import of resource properties.
+    -->
+    <propertymanager>
+        <!-- class element defines the manager to be used. The specified class
+             must implement the PropertyManager interface.
+             Note, that the handlers are being added and called in the order
+             they appear in the configuration.
+        -->
+        <class name="org.apache.jackrabbit.server.io.PropertyManagerImpl" />
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHandler" />
+        </propertyhandler>
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.VersionHistoryHandler" />
+        </propertyhandler>
+<!--         <propertyhandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.ZipHandler" /> -->
+<!--         </propertyhandler> -->
+<!--         <propertyhandler> -->
+<!--             <class name="org.apache.jackrabbit.server.io.XmlHandler" /> -->
+<!--         </propertyhandler> -->
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
+        </propertyhandler>
+    </propertymanager>
+    <!--
+     Define nodetypes, that should never by displayed as 'collection'
+    -->
+    <noncollection>
+        <nodetypes>
+            <nodetype>nt:file</nodetype>
+            <nodetype>nt:resource</nodetype>
+        </nodetypes>
+    </noncollection>
+    <!--
+     Example: Defines nodetypes, that should always be displayed as 'collection'.
+    -->
+    <!--
+    <collection>
+        <nodetypes>
+            <nodetype>nt:folder</nodetype>
+            <nodetype>rep:root</nodetype>
+        </nodetypes>
+    </collection>
+    -->
+    <!--
+     Filter that allows to prevent certain items from being displayed.
+     Please note, that this has an effect on PROPFIND calls only and does not
+     provide limited access to those items matching any of the filters.
+
+     However specifying a filter may cause problems with PUT or MKCOL if the
+     resource to be created is being filtered out, thus resulting in inconsistent
+     responses (e.g. PUT followed by PROPFIND on parent).
+     -->
+    <filter>
+        <!-- class element defines the resource filter to be used. The specified class
+             must implement the ItemFilter interface -->
+        <class name="org.apache.jackrabbit.webdav.simple.DefaultItemFilter" />
+        <!--
+         Nodetype names to be used to filter child nodes.
+         A child node can be filtered if the declaring nodetype of its definition
+         is one of the nodetype names specified in the nodetypes Element.
+         E.g. defining 'rep:root' as filtered nodetype whould result in jcr:system
+         being hidden but no other child node of the root node, since those
+         are defined by the nodetype nt:unstructered.
+        -->
+        <!--
+        <nodetypes>
+            <nodetype>rep:root</nodetype>
+        </nodetypes>
+        -->
+        <!--
+         Namespace prefixes or uris. Items having a name that matches any of the
+         entries will be filtered.
+        -->
+        <namespaces>
+            <prefix>rep</prefix>
+            <prefix>jcr</prefix>
+            <!--
+            <uri>internal</uri>
+            <uri>http://www.jcp.org/jcr/1.0</uri>
+            -->
+        </namespaces>
+    </filter>
+    
+    <!--
+     Optional 'mimetypeproperties' element.
+     It defines additional or replaces existing mappings for the MimeResolver
+     instance created by the ResourceConfig.
+     The default mappings are defined in org.apache.jackrabbit.server.io.mimetypes.properties.
+     If the default mime type defined by MimeResolver is 'application/octet-stream'.
+    -->
+    <!--
+    <mimetypeproperties>
+        <mimemapping extension="rtf" mimetype="application/rtf" />
+        <mimemapping extension="ott" mimetype="application/vnd.oasis.opendocument.text-template" />
+        <defaultmimetype>text/html</defaultmimetype>
+    </mimetypeproperties>
+    -->
+</config>
index 1d1734d36645664b0bc5adca04183674614daed0..6faa4c9e2ee2e36ed69150949ab5e528548d3ecf 100644 (file)
@@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.apache.jackrabbit.core.RepositoryContext;
 import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.http.NodeHttp;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.node.DataModelNamespace;
 import org.argeo.node.NodeConstants;
@@ -39,13 +40,12 @@ import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.cm.ManagedService;
-import org.osgi.service.http.HttpService;
 import org.osgi.service.useradmin.UserAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 
 public class CmsDeployment implements NodeDeployment {
        private final static String LEGACY_JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias";
-       
+
        private final Log log = LogFactory.getLog(getClass());
        private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
 
@@ -55,6 +55,9 @@ public class CmsDeployment implements NodeDeployment {
        private Long availableSince;
 
        private final boolean cleanState;
+
+       private NodeHttp nodeHttp;
+
        // Readiness
        private boolean nodeAvailable = false;
        private boolean userAdminAvailable = false;
@@ -69,11 +72,20 @@ public class CmsDeployment implements NodeDeployment {
                NodeState nodeState = bc.getService(nodeStateSr);
                cleanState = nodeState.isClean();
 
+               nodeHttp = new NodeHttp();
                initTrackers();
        }
 
        private void initTrackers() {
-               new PrepareHttpStc().open();
+               new ServiceTracker<NodeHttp, NodeHttp>(bc, NodeHttp.class, null) {
+
+                       @Override
+                       public NodeHttp addingService(ServiceReference<NodeHttp> reference) {
+                               httpAvailable = true;
+                               checkReadiness();
+                               return super.addingService(reference);
+                       }
+               }.open();
                new RepositoryContextStc().open();
                new ServiceTracker<UserAdmin, UserAdmin>(bc, UserAdmin.class, null) {
                        @Override
@@ -90,10 +102,11 @@ public class CmsDeployment implements NodeDeployment {
                                deployConfig = new DeployConfig(configurationAdmin, cleanState);
                                httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null;
                                try {
-                                       Configuration[] configs= configurationAdmin.listConfigurations("(service.factoryPid="+NodeConstants.NODE_REPOS_FACTORY_PID+")");
-                                       for(Configuration config:configs){
+                                       Configuration[] configs = configurationAdmin
+                                                       .listConfigurations("(service.factoryPid=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")");
+                                       for (Configuration config : configs) {
                                                Object cn = config.getProperties().get(NodeConstants.CN);
-                                               log.debug("Standalone repo cn: "+cn);
+                                               log.debug("Standalone repo cn: " + cn);
                                        }
                                } catch (Exception e) {
                                        throw new CmsException("Cannot initialize config", e);
@@ -104,6 +117,8 @@ public class CmsDeployment implements NodeDeployment {
        }
 
        public void shutdown() {
+               if(nodeHttp!=null)
+                       nodeHttp.destroy();
                if (deployConfig != null)
                        deployConfig.save();
        }
@@ -265,7 +280,7 @@ public class CmsDeployment implements NodeDeployment {
                                        prepareHomeRepository(nodeRepo.getRepository());
                                        nodeAvailable = true;
                                        checkReadiness();
-                               }else{
+                               } else {
                                        // TODO standalone
                                }
                        }
@@ -282,46 +297,4 @@ public class CmsDeployment implements NodeDeployment {
 
        }
 
-       private class PrepareHttpStc extends ServiceTracker<HttpService, HttpService> {
-               private DataHttp dataHttp;
-               private NodeHttp nodeHttp;
-
-               public PrepareHttpStc() {
-                       super(bc, HttpService.class, null);
-               }
-
-               @Override
-               public HttpService addingService(ServiceReference<HttpService> reference) {
-                       HttpService httpService = addHttpService(reference);
-                       return httpService;
-               }
-
-               @Override
-               public void removedService(ServiceReference<HttpService> reference, HttpService service) {
-                       if (dataHttp != null)
-                               dataHttp.destroy();
-                       dataHttp = null;
-                       if (nodeHttp != null)
-                               nodeHttp.destroy();
-                       nodeHttp = null;
-               }
-
-               private HttpService addHttpService(ServiceReference<HttpService> sr) {
-                       HttpService httpService = bc.getService(sr);
-                       // TODO find constants
-                       Object httpPort = sr.getProperty("http.port");
-                       Object httpsPort = sr.getProperty("https.port");
-                       dataHttp = new DataHttp(httpService);
-                       nodeHttp = new NodeHttp(httpService, bc);
-                       log.info(httpPortsMsg(httpPort, httpsPort));
-                       httpAvailable = true;
-                       checkReadiness();
-                       return httpService;
-               }
-
-               private String httpPortsMsg(Object httpPort, Object httpsPort) {
-                       return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : "");
-               }
-       }
-
 }
index 04db97833798f6b507952a1c73e47736dc626a90..f3d91ee4532561d9e31d2f4e3c8e08b9018c3c71 100644 (file)
@@ -40,8 +40,8 @@ public class CmsFsProvider extends AbstractJackrabbitFsProvider {
                        throw new FileSystemAlreadyExistsException("CMS file system already exists for user " + username);
 
                try {
-                       Repository repository = bc
-                                       .getService(bc.getServiceReferences(Repository.class, "(cn=node)").iterator().next());
+                       Repository repository = bc.getService(
+                                       bc.getServiceReferences(Repository.class, "(cn=" + NodeConstants.HOME + ")").iterator().next());
                        Session session = repository.login();
                        JcrFileSystem fileSystem = new JcrFileSystem(this, session);
                        fileSystems.put(username, fileSystem);
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataHttp.java
deleted file mode 100644 (file)
index bd44446..0000000
+++ /dev/null
@@ -1,579 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.URL;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.CredentialNotFoundException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.server.SessionProvider;
-import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
-import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.HttpRequestCallback;
-import org.argeo.cms.auth.HttpRequestCallbackHandler;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.node.NodeConstants;
-import org.ietf.jgss.GSSContext;
-import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
-import org.ietf.jgss.Oid;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.HttpService;
-import org.osgi.service.http.NamespaceException;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-
-/**
- * Intercepts and enriches http access, mainly focusing on security and
- * transactionality.
- */
-class DataHttp implements KernelConstants {
-       private final static Log log = LogFactory.getLog(DataHttp.class);
-
-       // private final static String ATTR_AUTH = "auth";
-       private final static String HEADER_AUTHORIZATION = "Authorization";
-       private final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
-
-       private final static String DEFAULT_PROTECTED_HANDLERS = "/org/argeo/cms/internal/kernel/protectedHandlers.xml";
-
-       private final BundleContext bc;
-       private final HttpService httpService;
-       private final ServiceTracker<Repository, Repository> repositories;
-
-       // FIXME Make it more unique
-       private String httpAuthRealm = "Argeo";
-
-       DataHttp(HttpService httpService) {
-               this.bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-               this.httpService = httpService;
-               repositories = new ServiceTracker<>(bc, Repository.class, new RepositoriesStc());
-               repositories.open();
-       }
-
-       public void destroy() {
-               repositories.close();
-       }
-
-       void registerRepositoryServlets(String alias, Repository repository) {
-               try {
-                       registerWebdavServlet(alias, repository);
-                       registerRemotingServlet(alias, repository);
-                       registerFilesServlet(alias, repository);
-                       if (log.isDebugEnabled())
-                               log.debug("Registered servlets for repository '" + alias + "'");
-               } catch (Exception e) {
-                       throw new CmsException("Could not register servlets for repository '" + alias + "'", e);
-               }
-       }
-
-       void unregisterRepositoryServlets(String alias) {
-               try {
-                       httpService.unregister(webdavPath(alias));
-                       httpService.unregister(remotingPath(alias));
-                       httpService.unregister(filesPath(alias));
-                       if (log.isDebugEnabled())
-                               log.debug("Unregistered servlets for repository '" + alias + "'");
-               } catch (Exception e) {
-                       log.error("Could not unregister servlets for repository '" + alias + "'", e);
-               }
-       }
-
-       void registerWebdavServlet(String alias, Repository repository) throws NamespaceException, ServletException {
-               WebdavServlet webdavServlet = new WebdavServlet(repository, new OpenInViewSessionProvider(alias));
-               String path = webdavPath(alias);
-               Properties ip = new Properties();
-               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, WEBDAV_CONFIG);
-               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
-               httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext());
-       }
-
-       void registerFilesServlet(String alias, Repository repository) throws NamespaceException, ServletException {
-               WebdavServlet filesServlet = new WebdavServlet(repository, new OpenInViewSessionProvider(alias));
-               String path = filesPath(alias);
-               Properties ip = new Properties();
-               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, WEBDAV_CONFIG);
-               ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
-               httpService.registerServlet(path, filesServlet, ip, new FilesHttpContext());
-       }
-
-       void registerRemotingServlet(String alias, Repository repository) throws NamespaceException, ServletException {
-               RemotingServlet remotingServlet = new RemotingServlet(repository, new OpenInViewSessionProvider(alias));
-               String path = remotingPath(alias);
-               Properties ip = new Properties();
-               ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
-
-               // Looks like a bug in Jackrabbit remoting init
-               ip.setProperty(RemotingServlet.INIT_PARAM_HOME, KernelUtils.getOsgiInstanceDir() + "/tmp/remoting_" + alias);
-               ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting_" + alias);
-               ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, DEFAULT_PROTECTED_HANDLERS);
-               ip.setProperty(RemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false");
-               httpService.registerServlet(path, remotingServlet, ip, new RemotingHttpContext());
-       }
-
-       private String webdavPath(String alias) {
-               return NodeConstants.PATH_DATA + "/" + alias;
-       }
-
-       private String remotingPath(String alias) {
-               return NodeConstants.PATH_JCR + "/" + alias;
-       }
-
-       private String filesPath(String alias) {
-               return NodeConstants.PATH_FILES + "/" + alias;
-       }
-
-       private Subject subjectFromRequest(HttpServletRequest request, HttpServletResponse response) {
-               Authorization authorization = (Authorization) request.getAttribute(HttpContext.AUTHORIZATION);
-               if (authorization == null)
-                       throw new CmsException("Not authenticated");
-               try {
-                       LoginContext lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
-                                       new HttpRequestCallbackHandler(request, response));
-                       lc.login();
-                       return lc.getSubject();
-               } catch (LoginException e) {
-                       throw new CmsException("Cannot login", e);
-               }
-       }
-
-       private void askForWwwAuth(HttpServletRequest request, HttpServletResponse response) {
-               response.setStatus(401);
-               response.setHeader(HEADER_WWW_AUTHENTICATE, "basic realm=\"" + httpAuthRealm + "\"");
-
-               // SPNEGO
-               // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate");
-               // 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");
-
-       }
-
-       private CallbackHandler extractHttpAuth(final HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
-               String authHeader = httpRequest.getHeader(HEADER_AUTHORIZATION);
-               if (authHeader != null) {
-                       StringTokenizer st = new StringTokenizer(authHeader);
-                       if (st.hasMoreTokens()) {
-                               String basic = st.nextToken();
-                               if (basic.equalsIgnoreCase("Basic")) {
-                                       try {
-                                               // TODO manipulate char[]
-                                               String credentials = new String(Base64.decodeBase64(st.nextToken()), "UTF-8");
-                                               // log.debug("Credentials: " + credentials);
-                                               int p = credentials.indexOf(":");
-                                               if (p != -1) {
-                                                       final String login = credentials.substring(0, p).trim();
-                                                       final char[] password = credentials.substring(p + 1).trim().toCharArray();
-                                                       return new CallbackHandler() {
-                                                               public void handle(Callback[] callbacks) {
-                                                                       for (Callback cb : callbacks) {
-                                                                               if (cb instanceof NameCallback)
-                                                                                       ((NameCallback) cb).setName(login);
-                                                                               else if (cb instanceof PasswordCallback)
-                                                                                       ((PasswordCallback) cb).setPassword(password);
-                                                                               else if (cb instanceof HttpRequestCallback) {
-                                                                                       ((HttpRequestCallback) cb).setRequest(httpRequest);
-                                                                                       ((HttpRequestCallback) cb).setResponse(httpResponse);
-                                                                               }
-                                                                       }
-                                                               }
-                                                       };
-                                               } else {
-                                                       throw new CmsException("Invalid authentication token");
-                                               }
-                                       } catch (Exception e) {
-                                               throw new CmsException("Couldn't retrieve authentication", e);
-                                       }
-                               } else if (basic.equalsIgnoreCase("Negotiate")) {
-                                       // FIXME generalise
-                                       String _targetName = "HTTP/mostar.desktop.argeo.pro";
-                                       String spnegoToken = st.nextToken();
-                                       byte[] authToken = Base64.decodeBase64(spnegoToken);
-                                       GSSManager manager = GSSManager.getInstance();
-                                       try {
-                                               Oid krb5Oid = new Oid("1.3.6.1.5.5.2"); // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
-                                               GSSName gssName = manager.createName(_targetName, null);
-                                               GSSCredential serverCreds = manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME,
-                                                               krb5Oid, GSSCredential.ACCEPT_ONLY);
-                                               GSSContext gContext = manager.createContext(serverCreds);
-
-                                               if (gContext == null) {
-                                                       log.debug("SpnegoUserRealm: failed to establish GSSContext");
-                                               } else {
-                                                       while (!gContext.isEstablished()) {
-                                                               byte[] outToken = gContext.acceptSecContext(authToken, 0, authToken.length);
-                                                               String outTokenStr = Base64.encodeBase64String(outToken);
-                                                               httpResponse.setHeader("WWW-Authenticate", "Negotiate " + outTokenStr);
-                                                       }
-                                                       if (gContext.isEstablished()) {
-                                                               String clientName = gContext.getSrcName().toString();
-                                                               String role = clientName.substring(clientName.indexOf('@') + 1);
-
-                                                               log.debug("SpnegoUserRealm: established a security context");
-                                                               log.debug("Client Principal is: " + gContext.getSrcName());
-                                                               log.debug("Server Principal is: " + gContext.getTargName());
-                                                               log.debug("Client Default Role: " + role);
-
-                                                               // TODO log in
-                                                       }
-                                               }
-
-                                       } catch (GSSException gsse) {
-                                               log.warn(gsse, gsse);
-                                       }
-
-                               }
-                       }
-               }
-               return null;
-       }
-
-       private class RepositoriesStc implements ServiceTrackerCustomizer<Repository, Repository> {
-
-               @Override
-               public Repository addingService(ServiceReference<Repository> reference) {
-                       Repository repository = bc.getService(reference);
-                       Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
-                       if (jcrRepoAlias != null) {
-                               String alias = jcrRepoAlias.toString();
-                               registerRepositoryServlets(alias, repository);
-                       }
-                       return repository;
-               }
-
-               @Override
-               public void modifiedService(ServiceReference<Repository> reference, Repository service) {
-               }
-
-               @Override
-               public void removedService(ServiceReference<Repository> reference, Repository service) {
-                       Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
-                       if (jcrRepoAlias != null) {
-                               String alias = jcrRepoAlias.toString();
-                               unregisterRepositoryServlets(alias);
-                       }
-               }
-       }
-
-       private class DataHttpContext implements HttpContext {
-               @Override
-               public boolean handleSecurity(final HttpServletRequest request, HttpServletResponse response)
-                               throws IOException {
-
-                       if (log.isTraceEnabled())
-                               KernelUtils.logRequestHeaders(log, request);
-                       LoginContext lc;
-                       try {
-                               lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
-                                               new HttpRequestCallbackHandler(request, response));
-                               lc.login();
-                               // return true;
-                       } catch (LoginException e) {
-                               CallbackHandler token = extractHttpAuth(request, response);
-                               if (token != null) {
-                                       try {
-                                               lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, token);
-                                               lc.login();
-                                               // Note: this is impossible to reliably clear the
-                                               // authorization header when access from a browser.
-                                               return true;
-                                       } catch (LoginException e1) {
-                                               throw new CmsException("Could not login", e1);
-                                       }
-                               } else {
-                                       // anonymous
-                                       try {
-                                               lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER);
-                                               lc.login();
-                                       } catch (LoginException e1) {
-                                               if (log.isDebugEnabled())
-                                                       log.error("Cannot log in anonynous", e1);
-                                               return false;
-                                       }
-                               }
-                       }
-                       request.setAttribute(NodeConstants.LOGIN_CONTEXT_USER, lc);
-                       return true;
-               }
-
-               @Override
-               public URL getResource(String name) {
-                       return KernelUtils.getBundleContext(DataHttp.class).getBundle().getResource(name);
-               }
-
-               @Override
-               public String getMimeType(String name) {
-                       return null;
-               }
-
-       }
-
-       private class FilesHttpContext implements HttpContext {
-               @Override
-               public boolean handleSecurity(final HttpServletRequest request, HttpServletResponse response)
-                               throws IOException {
-
-                       if (log.isTraceEnabled())
-                               KernelUtils.logRequestHeaders(log, request);
-                       LoginContext lc;
-                       try {
-                               lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
-                                               new HttpRequestCallbackHandler(request, response));
-                               lc.login();
-                               // return true;
-                       } catch (LoginException e) {
-                               CallbackHandler token = extractHttpAuth(request, response);
-                               if (token != null) {
-                                       try {
-                                               lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, token);
-                                               lc.login();
-                                               // Note: this is impossible to reliably clear the
-                                               // authorization header when access from a browser.
-                                       } catch (LoginException e1) {
-                                               throw new CmsException("Could not login", e1);
-                                       }
-                               } else {
-                                       askForWwwAuth(request, response);
-                                       lc = null;
-                                       return false;
-                               }
-                       }
-                       request.setAttribute(NodeConstants.LOGIN_CONTEXT_USER, lc);
-                       return true;
-               }
-
-               @Override
-               public URL getResource(String name) {
-                       return KernelUtils.getBundleContext(DataHttp.class).getBundle().getResource(name);
-               }
-
-               @Override
-               public String getMimeType(String name) {
-                       return null;
-               }
-
-       }
-
-       private class RemotingHttpContext implements HttpContext {
-               // private final boolean anonymous;
-
-               RemotingHttpContext() {
-                       // this.anonymous = anonymous;
-               }
-
-               @Override
-               public boolean handleSecurity(final HttpServletRequest request, HttpServletResponse response)
-                               throws IOException {
-
-                       // if (anonymous) {
-                       // Subject subject = KernelUtils.anonymousLogin();
-                       // Authorization authorization =
-                       // subject.getPrivateCredentials(Authorization.class).iterator().next();
-                       // request.setAttribute(REMOTE_USER, NodeConstants.ROLE_ANONYMOUS);
-                       // request.setAttribute(AUTHORIZATION, authorization);
-                       // return true;
-                       // }
-
-                       if (log.isTraceEnabled())
-                               KernelUtils.logRequestHeaders(log, request);
-                       LoginContext lc;
-                       try {
-                               lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
-                                               new HttpRequestCallbackHandler(request, response));
-                               lc.login();
-                       } catch (CredentialNotFoundException e) {
-                               CallbackHandler token = extractHttpAuth(request, response);
-                               if (token != null) {
-                                       try {
-                                               lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, token);
-                                               lc.login();
-                                               // Note: this is impossible to reliably clear the
-                                               // authorization header when access from a browser.
-                                       } catch (LoginException e1) {
-                                               throw new CmsException("Could not login", e1);
-                                       }
-                               } else {
-                                       askForWwwAuth(request, response);
-                                       lc = null;
-                               }
-                       } catch (LoginException e) {
-                               throw new CmsException("Could not login", e);
-                       }
-
-                       if (lc != null) {
-                               request.setAttribute(NodeConstants.LOGIN_CONTEXT_USER, lc);
-                               return true;
-                       } else {
-                               return false;
-                       }
-               }
-
-               @Override
-               public URL getResource(String name) {
-                       return KernelUtils.getBundleContext(DataHttp.class).getBundle().getResource(name);
-               }
-
-               @Override
-               public String getMimeType(String name) {
-                       return null;
-               }
-
-       }
-
-       /**
-        * Implements an open session in view patter: a new JCR session is created
-        * for each request
-        */
-       private class OpenInViewSessionProvider implements SessionProvider, Serializable {
-               private static final long serialVersionUID = 2270957712453841368L;
-               private final String alias;
-
-               public OpenInViewSessionProvider(String alias) {
-                       this.alias = alias;
-               }
-
-               public Session getSession(HttpServletRequest request, Repository rep, String workspace)
-                               throws javax.jcr.LoginException, ServletException, RepositoryException {
-                       return login(request, rep, workspace);
-               }
-
-               protected Session login(HttpServletRequest request, Repository repository, String workspace)
-                               throws RepositoryException {
-                       if (log.isTraceEnabled())
-                               log.trace("Repo " + alias + ", login to workspace " + (workspace == null ? "<default>" : workspace)
-                                               + " in web session " + request.getSession().getId());
-                       LoginContext lc = (LoginContext) request.getAttribute(NodeConstants.LOGIN_CONTEXT_USER);
-                       if (lc == null)
-                               throw new CmsException("No login context available");
-                       try {
-                               // LoginContext lc = new
-                               // LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
-                               // new HttpRequestCallbackHandler(request));
-                               // lc.login();
-                               return Subject.doAs(lc.getSubject(), new PrivilegedExceptionAction<Session>() {
-                                       @Override
-                                       public Session run() throws Exception {
-                                               return repository.login(workspace);
-                                       }
-                               });
-                       } catch (Exception e) {
-                               throw new CmsException("Cannot log in to JCR", e);
-                       }
-                       // return repository.login(workspace);
-               }
-
-               public void releaseSession(Session session) {
-                       JcrUtils.logoutQuietly(session);
-                       if (log.isTraceEnabled())
-                               log.trace("Logged out remote JCR session " + session);
-               }
-       }
-
-       private class WebdavServlet extends SimpleWebdavServlet {
-               private static final long serialVersionUID = -4687354117811443881L;
-               private final Repository repository;
-
-               public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
-                       this.repository = repository;
-                       setSessionProvider(sessionProvider);
-               }
-
-               public Repository getRepository() {
-                       return repository;
-               }
-
-               @Override
-               protected void service(final HttpServletRequest request, final HttpServletResponse response)
-                               throws ServletException, IOException {
-                       WebdavServlet.super.service(request, response);
-                       // try {
-                       // Subject subject = subjectFromRequest(request);
-                       // // TODO make it stronger, with eTags.
-                       // // if (CurrentUser.isAnonymous(subject) &&
-                       // // request.getMethod().equals("GET")) {
-                       // // response.setHeader("Cache-Control", "no-transform, public,
-                       // // max-age=300, s-maxage=900");
-                       // // }
-                       //
-                       // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
-                       // @Override
-                       // public Void run() throws Exception {
-                       // WebdavServlet.super.service(request, response);
-                       // return null;
-                       // }
-                       // });
-                       // } catch (PrivilegedActionException e) {
-                       // throw new CmsException("Cannot process webdav request",
-                       // e.getException());
-                       // }
-               }
-       }
-
-       private class RemotingServlet extends JcrRemotingServlet {
-               private static final long serialVersionUID = 4605238259548058883L;
-               private final Repository repository;
-               private final SessionProvider sessionProvider;
-
-               public RemotingServlet(Repository repository, SessionProvider sessionProvider) {
-                       this.repository = repository;
-                       this.sessionProvider = sessionProvider;
-               }
-
-               @Override
-               protected Repository getRepository() {
-                       return repository;
-               }
-
-               @Override
-               protected SessionProvider getSessionProvider() {
-                       return sessionProvider;
-               }
-
-               @Override
-               protected void service(final HttpServletRequest request, final HttpServletResponse response)
-                               throws ServletException, IOException {
-                       try {
-                               Subject subject = subjectFromRequest(request, response);
-                               Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
-                                       @Override
-                                       public Void run() throws Exception {
-                                               RemotingServlet.super.service(request, response);
-                                               return null;
-                                       }
-                               });
-                       } catch (PrivilegedActionException e) {
-                               throw new CmsException("Cannot process JCR remoting request", e.getException());
-                       }
-               }
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/HttpFilter.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/HttpFilter.java
deleted file mode 100644 (file)
index b8d2ae5..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import java.io.IOException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-/** Abstract base class for http filters. */
-abstract class HttpFilter implements Filter {
-       // private final static Log log = LogFactory.getLog(HttpFilter.class);
-
-       protected abstract void doFilter(HttpSession httpSession,
-                       HttpServletRequest request, HttpServletResponse response,
-                       FilterChain filterChain) throws IOException, ServletException;
-
-       @Override
-       public void doFilter(ServletRequest servletRequest,
-                       ServletResponse servletResponse, FilterChain filterChain)
-                       throws IOException, ServletException {
-               HttpServletRequest request = (HttpServletRequest) servletRequest;
-               doFilter(request.getSession(), request,
-                               (HttpServletResponse) servletResponse, filterChain);
-       }
-
-       @Override
-       public void destroy() {
-       }
-
-       @Override
-       public void init(FilterConfig arg0) throws ServletException {
-       }
-
-}
index 2e0c371e09ee69e1b0201494ae679ed9876cf2c8..b9671f2a4e08c958b3b4c1b131acd91b60d8ec66 100644 (file)
@@ -1,6 +1,7 @@
 package org.argeo.cms.internal.kernel;
 
 /** The available Jackrabbit node types */
+@Deprecated
 public enum JackrabbitType {
        localfs, h2, postgresql, postgresql_ds,postgresql_cluster, memory;
 }
index cbe46b45308d8cbfeaf5128731f1af600a5b0421..c3cf0d1bfcdb05df299549619be83e61e115807a 100644 (file)
@@ -24,7 +24,7 @@ public interface KernelConstants {
        // String LOGIN_CONTEXT_HARDENED_KERNEL = "HARDENED_KERNEL";
 
        // DAV
-       String WEBDAV_CONFIG = "/org/argeo/cms/internal/kernel/webdav-config.xml";
+//     String WEBDAV_CONFIG = "/org/argeo/cms/internal/http/webdav-config.xml";
        // String PATH_DATA = "/data";
        // String WEBDAV_PUBLIC = PATH_DATA + "/public";
        // String WEBDAV_PRIVATE = PATH_DATA + "/files";
index e3bbdb79629d1cd8ed8a9d7837dce0b859a6fc3c..5b1df1708ab47ad8d7114bb084ba01ac12daa20a 100644 (file)
@@ -111,29 +111,17 @@ class KernelUtils implements KernelConstants {
        }
 
        // Security
-       static Subject anonymousLogin() {
-               Subject subject = new Subject();
-               LoginContext lc;
-               try {
-                       lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject);
-                       lc.login();
-                       return subject;
-               } catch (LoginException e) {
-                       throw new CmsException("Cannot login as anonymous", e);
-               }
-       }
-
-       // HTTP
-       static void logRequestHeaders(Log log, HttpServletRequest request) {
-               if (!log.isDebugEnabled())
-                       return;
-               for (Enumeration<String> headerNames = request.getHeaderNames(); headerNames.hasMoreElements();) {
-                       String headerName = headerNames.nextElement();
-                       Object headerValue = request.getHeader(headerName);
-                       log.debug(headerName + ": " + headerValue);
-               }
-               log.debug(request.getRequestURI() + "\n");
-       }
+//     static Subject anonymousLogin() {
+//             Subject subject = new Subject();
+//             LoginContext lc;
+//             try {
+//                     lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject);
+//                     lc.login();
+//                     return subject;
+//             } catch (LoginException e) {
+//                     throw new CmsException("Cannot login as anonymous", e);
+//             }
+//     }
 
        static void logFrameworkProperties(Log log) {
                BundleContext bc = getBundleContext();
@@ -150,8 +138,8 @@ class KernelUtils implements KernelConstants {
                // for (String key : keys)
                // log.debug(key + "=" + bc.getProperty(key));
        }
-       
-       static void printSystemProperties(PrintStream out){
+
+       static void printSystemProperties(PrintStream out) {
                TreeMap<String, String> display = new TreeMap<>();
                for (Object key : System.getProperties().keySet())
                        display.put(key.toString(), System.getProperty(key.toString()));
@@ -217,7 +205,6 @@ class KernelUtils implements KernelConstants {
                }
        }
 
-
        private KernelUtils() {
 
        }
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
deleted file mode 100644 (file)
index f17e157..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import static javax.jcr.Property.JCR_DESCRIPTION;
-import static javax.jcr.Property.JCR_LAST_MODIFIED;
-import static javax.jcr.Property.JCR_TITLE;
-import static org.argeo.cms.CmsTypes.CMS_IMAGE;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.security.PrivilegedExceptionAction;
-import java.security.cert.X509Certificate;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Enumeration;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.security.auth.Subject;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.node.NodeConstants;
-import org.argeo.node.NodeUtils;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.HttpService;
-
-/**
- * Intercepts and enriches http access, mainly focusing on security and
- * transactionality.
- */
-class NodeHttp implements KernelConstants {
-       private final static Log log = LogFactory.getLog(NodeHttp.class);
-
-       // Filters
-       // private final RootFilter rootFilter;
-
-       // private final DoSFilter dosFilter;
-       // private final QoSFilter qosFilter;
-
-       private BundleContext bc;
-
-       NodeHttp(HttpService httpService, BundleContext bc) {
-               this.bc = bc;
-               // rootFilter = new RootFilter();
-               // dosFilter = new CustomDosFilter();
-               // qosFilter = new QoSFilter();
-
-               try {
-                       httpService.registerServlet("/!", new LinkServlet(), null, null);
-                       httpService.registerServlet("/robots.txt", new RobotServlet(), null, null);
-               } catch (Exception e) {
-                       throw new CmsException("Cannot register filters", e);
-               }
-       }
-
-       public void destroy() {
-       }
-
-       class LinkServlet extends HttpServlet {
-               private static final long serialVersionUID = 3749990143146845708L;
-
-               @Override
-               protected void service(HttpServletRequest request, HttpServletResponse response)
-                               throws ServletException, IOException {
-                       String path = request.getPathInfo();
-                       String userAgent = request.getHeader("User-Agent").toLowerCase();
-                       boolean isBot = false;
-                       boolean isCompatibleBrowser = false;
-                       if (userAgent.contains("bot") || userAgent.contains("facebook") || userAgent.contains("twitter")) {
-                               isBot = true;
-                       } else if (userAgent.contains("webkit") || userAgent.contains("gecko") || userAgent.contains("firefox")
-                                       || userAgent.contains("msie") || userAgent.contains("chrome") || userAgent.contains("chromium")
-                                       || userAgent.contains("opera") || userAgent.contains("browser")) {
-                               isCompatibleBrowser = true;
-                       }
-
-                       if (isBot) {
-                               log.warn("# BOT " + request.getHeader("User-Agent"));
-                               canonicalAnswer(request, response, path);
-                               return;
-                       }
-
-                       if (isCompatibleBrowser && log.isTraceEnabled())
-                               log.trace("# BWS " + request.getHeader("User-Agent"));
-                       redirectTo(response, "/#" + path);
-               }
-
-               private void redirectTo(HttpServletResponse response, String location) {
-                       response.setHeader("Location", location);
-                       response.setStatus(HttpServletResponse.SC_FOUND);
-               }
-
-               // private boolean canonicalAnswerNeededBy(HttpServletRequest request) {
-               // String userAgent = request.getHeader("User-Agent").toLowerCase();
-               // return userAgent.startsWith("facebookexternalhit/");
-               // }
-
-               /** For bots which don't understand RWT. */
-               private void canonicalAnswer(HttpServletRequest request, HttpServletResponse response, String path) {
-                       Session session = null;
-                       try {
-                               PrintWriter writer = response.getWriter();
-                               session = Subject.doAs(KernelUtils.anonymousLogin(), new PrivilegedExceptionAction<Session>() {
-
-                                       @Override
-                                       public Session run() throws Exception {
-                                               Collection<ServiceReference<Repository>> srs = bc.getServiceReferences(Repository.class,
-                                                               "(" + NodeConstants.CN + "=" + NodeConstants.NODE + ")");
-                                               Repository repository = bc.getService(srs.iterator().next());
-                                               return repository.login();
-                                       }
-
-                               });
-                               Node node = session.getNode(path);
-                               String title = node.hasProperty(JCR_TITLE) ? node.getProperty(JCR_TITLE).getString() : node.getName();
-                               String desc = node.hasProperty(JCR_DESCRIPTION) ? node.getProperty(JCR_DESCRIPTION).getString() : null;
-                               Calendar lastUpdate = node.hasProperty(JCR_LAST_MODIFIED)
-                                               ? node.getProperty(JCR_LAST_MODIFIED).getDate() : null;
-                               String url = getCanonicalUrl(node, request);
-                               String imgUrl = null;
-                               loop: for (NodeIterator it = node.getNodes(); it.hasNext();) {
-                                       // Takes the first found cms:image
-                                       Node child = it.nextNode();
-                                       if (child.isNodeType(CMS_IMAGE)) {
-                                               imgUrl = getDataUrl(child, request);
-                                               break loop;
-                                       }
-                               }
-                               StringBuilder buf = new StringBuilder();
-                               buf.append("<html>");
-                               buf.append("<head>");
-                               writeMeta(buf, "og:title", escapeHTML(title));
-                               writeMeta(buf, "og:type", "website");
-                               buf.append("<meta name='twitter:card' content='summary' />");
-                               buf.append("<meta name='twitter:site' content='@argeo_org' />");
-                               writeMeta(buf, "og:url", url);
-                               if (desc != null)
-                                       writeMeta(buf, "og:description", escapeHTML(desc));
-                               if (imgUrl != null)
-                                       writeMeta(buf, "og:image", imgUrl);
-                               if (lastUpdate != null)
-                                       writeMeta(buf, "og:updated_time", Long.toString(lastUpdate.getTime().getTime()));
-                               buf.append("</head>");
-                               buf.append("<body>");
-                               buf.append(
-                                               "<p><b>!! This page is meant for indexing robots, not for real people," + " visit <a href='/#")
-                                               .append(path).append("'>").append(escapeHTML(title)).append("</a> instead.</b></p>");
-                               writeCanonical(buf, node);
-                               buf.append("</body>");
-                               buf.append("</html>");
-                               writer.print(buf.toString());
-
-                               response.setHeader("Content-Type", "text/html");
-                               writer.flush();
-                       } catch (Exception e) {
-                               throw new CmsException("Cannot write canonical answer", e);
-                       } finally {
-                               JcrUtils.logoutQuietly(session);
-                       }
-               }
-
-               /**
-                * From
-                * http://stackoverflow.com/questions/1265282/recommended-method-for-
-                * escaping-html-in-java (+ escaping '). TODO Use
-                * org.apache.commons.lang.StringEscapeUtils
-                */
-               private String escapeHTML(String s) {
-                       StringBuilder out = new StringBuilder(Math.max(16, s.length()));
-                       for (int i = 0; i < s.length(); i++) {
-                               char c = s.charAt(i);
-                               if (c > 127 || c == '\'' || c == '"' || c == '<' || c == '>' || c == '&') {
-                                       out.append("&#");
-                                       out.append((int) c);
-                                       out.append(';');
-                               } else {
-                                       out.append(c);
-                               }
-                       }
-                       return out.toString();
-               }
-
-               private void writeMeta(StringBuilder buf, String tag, String value) {
-                       buf.append("<meta property='").append(tag).append("' content='").append(value).append("'/>");
-               }
-
-               private void writeCanonical(StringBuilder buf, Node node) throws RepositoryException {
-                       buf.append("<div>");
-                       if (node.hasProperty(JCR_TITLE))
-                               buf.append("<p>").append(node.getProperty(JCR_TITLE).getString()).append("</p>");
-                       if (node.hasProperty(JCR_DESCRIPTION))
-                               buf.append("<p>").append(node.getProperty(JCR_DESCRIPTION).getString()).append("</p>");
-                       NodeIterator children = node.getNodes();
-                       while (children.hasNext()) {
-                               writeCanonical(buf, children.nextNode());
-                       }
-                       buf.append("</div>");
-               }
-
-               // DATA
-               private StringBuilder getServerBaseUrl(HttpServletRequest request) {
-                       try {
-                               URL url = new URL(request.getRequestURL().toString());
-                               StringBuilder buf = new StringBuilder();
-                               buf.append(url.getProtocol()).append("://").append(url.getHost());
-                               if (url.getPort() != -1)
-                                       buf.append(':').append(url.getPort());
-                               return buf;
-                       } catch (MalformedURLException e) {
-                               throw new CmsException("Cannot extract server base URL from " + request.getRequestURL(), e);
-                       }
-               }
-
-               private String getDataUrl(Node node, HttpServletRequest request) throws RepositoryException {
-                       try {
-                               StringBuilder buf = getServerBaseUrl(request);
-                               buf.append(NodeUtils.getDataPath(NodeConstants.NODE, node));
-                               return new URL(buf.toString()).toString();
-                       } catch (MalformedURLException e) {
-                               throw new CmsException("Cannot build data URL for " + node, e);
-                       }
-               }
-
-               // public static String getDataPath(Node node) throws
-               // RepositoryException {
-               // assert node != null;
-               // String userId = node.getSession().getUserID();
-               //// if (log.isTraceEnabled())
-               //// log.trace(userId + " : " + node.getPath());
-               // StringBuilder buf = new StringBuilder();
-               // boolean isAnonymous =
-               // userId.equalsIgnoreCase(NodeConstants.ROLE_ANONYMOUS);
-               // if (isAnonymous)
-               // buf.append(WEBDAV_PUBLIC);
-               // else
-               // buf.append(WEBDAV_PRIVATE);
-               // Session session = node.getSession();
-               // Repository repository = session.getRepository();
-               // String cn;
-               // if (repository.isSingleValueDescriptor(NodeConstants.CN)) {
-               // cn = repository.getDescriptor(NodeConstants.CN);
-               // } else {
-               //// log.warn("No cn defined in repository, using " +
-               // NodeConstants.NODE);
-               // cn = NodeConstants.NODE;
-               // }
-               // return
-               // buf.append('/').append(cn).append('/').append(session.getWorkspace().getName()).append(node.getPath())
-               // .toString();
-               // }
-
-               private String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
-                       try {
-                               StringBuilder buf = getServerBaseUrl(request);
-                               buf.append('/').append('!').append(node.getPath());
-                               return new URL(buf.toString()).toString();
-                       } catch (MalformedURLException e) {
-                               throw new CmsException("Cannot build data URL for " + node, e);
-                       }
-                       // return request.getRequestURL().append('!').append(node.getPath())
-                       // .toString();
-               }
-
-       }
-
-       class RobotServlet extends HttpServlet {
-               private static final long serialVersionUID = 7935661175336419089L;
-
-               @Override
-               protected void service(HttpServletRequest request, HttpServletResponse response)
-                               throws ServletException, IOException {
-                       PrintWriter writer = response.getWriter();
-                       writer.append("User-agent: *\n");
-                       writer.append("Disallow:\n");
-                       response.setHeader("Content-Type", "text/plain");
-                       writer.flush();
-               }
-
-       }
-
-       /** Intercepts all requests. Authenticates. */
-       class RootFilter extends HttpFilter {
-
-               @Override
-               public void doFilter(HttpSession httpSession, HttpServletRequest request, HttpServletResponse response,
-                               FilterChain filterChain) throws IOException, ServletException {
-                       if (log.isTraceEnabled()) {
-                               log.trace(request.getRequestURL()
-                                               .append(request.getQueryString() != null ? "?" + request.getQueryString() : ""));
-                               logRequest(request);
-                       }
-
-                       String servletPath = request.getServletPath();
-
-                       // client certificate
-                       X509Certificate clientCert = extractCertificate(request);
-                       if (clientCert != null) {
-                               // TODO authenticate
-                               // if (log.isDebugEnabled())
-                               // log.debug(clientCert.getSubjectX500Principal().getName());
-                       }
-
-                       // skip data
-                       if (servletPath.startsWith(NodeConstants.PATH_DATA)) {
-                               filterChain.doFilter(request, response);
-                               return;
-                       }
-
-                       // skip /ui (workbench) for the time being
-                       if (servletPath.startsWith(PATH_WORKBENCH)) {
-                               filterChain.doFilter(request, response);
-                               return;
-                       }
-
-                       // redirect long RWT paths to anchor
-                       String path = request.getRequestURI().substring(servletPath.length());
-                       int pathLength = path.length();
-                       if (pathLength != 0 && (path.charAt(0) == '/') && !servletPath.endsWith("rwt-resources")
-                                       && !path.startsWith(KernelConstants.PATH_WORKBENCH) && path.lastIndexOf('/') != 0) {
-                               String newLocation = request.getServletPath() + "#" + path;
-                               response.setHeader("Location", newLocation);
-                               response.setStatus(HttpServletResponse.SC_FOUND);
-                               return;
-                       }
-
-                       // process normally
-                       filterChain.doFilter(request, response);
-               }
-       }
-
-       private void logRequest(HttpServletRequest request) {
-               log.debug("contextPath=" + request.getContextPath());
-               log.debug("servletPath=" + request.getServletPath());
-               log.debug("requestURI=" + request.getRequestURI());
-               log.debug("queryString=" + request.getQueryString());
-               StringBuilder buf = new StringBuilder();
-               // headers
-               Enumeration<String> en = request.getHeaderNames();
-               while (en.hasMoreElements()) {
-                       String header = en.nextElement();
-                       Enumeration<String> values = request.getHeaders(header);
-                       while (values.hasMoreElements())
-                               buf.append("  " + header + ": " + values.nextElement());
-                       buf.append('\n');
-               }
-
-               // attributed
-               Enumeration<String> an = request.getAttributeNames();
-               while (an.hasMoreElements()) {
-                       String attr = an.nextElement();
-                       Object value = request.getAttribute(attr);
-                       buf.append("  " + attr + ": " + value);
-                       buf.append('\n');
-               }
-               log.debug("\n" + buf);
-       }
-
-       private X509Certificate extractCertificate(HttpServletRequest req) {
-               X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
-               if (null != certs && certs.length > 0) {
-                       return certs[0];
-               }
-               return null;
-       }
-
-       // class CustomDosFilter extends DoSFilter {
-       // @Override
-       // protected String extractUserId(ServletRequest request) {
-       // HttpSession httpSession = ((HttpServletRequest) request)
-       // .getSession();
-       // if (isSessionAuthenticated(httpSession)) {
-       // String userId = ((SecurityContext) httpSession
-       // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
-       // .getAuthentication().getName();
-       // return userId;
-       // }
-       // return super.extractUserId(request);
-       //
-       // }
-       // }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/WebCmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/WebCmsSessionImpl.java
deleted file mode 100644 (file)
index cff723e..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Hashtable;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.auth.WebCmsSession;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.useradmin.Authorization;
-
-public class WebCmsSessionImpl implements WebCmsSession {
-       private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-       private final static Log log = LogFactory.getLog(WebCmsSessionImpl.class);
-
-       private final String id;
-       private final Authorization authorization;
-
-       private List<SubHttpSession> subHttpSessions = new ArrayList<>();
-
-       private ServiceRegistration<WebCmsSession> serviceRegistration;
-
-       public WebCmsSessionImpl(String sessionId, Authorization authorization) {
-               this.id = sessionId;
-               this.authorization = authorization;
-               // register as service
-               Hashtable<String, String> props = new Hashtable<>();
-               props.put(WebCmsSession.CMS_DN, authorization.getName());
-               props.put(WebCmsSession.CMS_SESSION_ID, sessionId);
-               serviceRegistration = bc.registerService(WebCmsSession.class, this, props);
-       }
-
-       public void cleanUp() {
-               for (SubHttpSession subSession : subHttpSessions)
-                       subSession.cleanUp();
-               serviceRegistration.unregister();
-       }
-
-       @Override
-       public Authorization getAuthorization() {
-               return authorization;
-       }
-
-       public ServiceRegistration<WebCmsSession> getServiceRegistration() {
-               return serviceRegistration;
-       }
-
-       public void addHttpSession(HttpServletRequest request) {
-               subHttpSessions.add(new SubHttpSession(request));
-       }
-
-       public String getId() {
-               return id;
-       }
-
-       public String toString() {
-               return "CMS Session #" + id;
-       }
-
-       static class SubHttpSession {
-               private final HttpSession httpSession;
-               private final String sessionId;
-               // private final String originalURI;
-               // private final String servletPath;
-
-               private final Date start = new Date();
-
-               public SubHttpSession(HttpServletRequest request) {
-                       this.httpSession = request.getSession();
-                       this.sessionId = httpSession.getId();
-                       // this.originalURI = request.getRequestURI();
-                       // this.servletPath = request.getServletPath();
-               }
-
-               public Date getStart() {
-                       return start;
-               }
-
-               public void cleanUp() {
-                       try {
-                               httpSession.setAttribute(HttpContext.REMOTE_USER, null);
-                               httpSession.setAttribute(HttpContext.AUTHORIZATION, null);
-                               // httpSession.setMaxInactiveInterval(1);
-                       } catch (Exception e) {
-                               log.warn("Could not clean up " + sessionId, e);
-                       }
-               }
-
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/protectedHandlers.xml b/org.argeo.cms/src/org/argeo/cms/internal/kernel/protectedHandlers.xml
deleted file mode 100644 (file)
index 59f22cd..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<config>
-       <protecteditemremovehandler>
-               <class name="org.apache.jackrabbit.server.remoting.davex.AclRemoveHandler" />
-       </protecteditemremovehandler>
-</config>
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/webdav-config.xml b/org.argeo.cms/src/org/argeo/cms/internal/kernel/webdav-config.xml
deleted file mode 100644 (file)
index da4e18b..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You 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.
-  -->
-<!--
-<!DOCTYPE config [
-        <!ELEMENT config (iomanager , propertymanager, (collection | noncollection)? , filter?, mimetypeproperties?) >
-
-        <!ELEMENT iomanager (class, iohandler*) >
-        <!ELEMENT iohandler (class) >
-
-        <!ELEMENT propertymanager (class, propertyhandler*) >
-        <!ELEMENT propertyhandler (class) >
-
-        <!ELEMENT collection (nodetypes) >
-        <!ELEMENT noncollection (nodetypes) >
-
-        <!ELEMENT filter (class, namespaces?, nodetypes?) >
-
-        <!ELEMENT class >
-        <!ATTLIST class
-            name  CDATA #REQUIRED
-        >
-        <!ELEMENT namespaces (prefix | uri)* >
-        <!ELEMENT prefix (CDATA) >
-        <!ELEMENT uri (CDATA) >
-
-        <!ELEMENT nodetypes (nodetype)* >
-        <!ELEMENT nodetype (CDATA) >
-
-        <!ELEMENT mimetypeproperties (mimemapping*, defaultmimetype) >
-
-        <!ELEMENT mimemapping >
-        <!ATTLIST mimemapping
-            extension  CDATA #REQUIRED
-            mimetype  CDATA #REQUIRED
-        >
-
-        <!ELEMENT defaultmimetype (CDATA) >
-]>
--->
-
-<config>
-    <!--
-     Defines the IOManager implementation that is responsible for passing
-     import/export request to the individual IO-handlers.
-    -->
-    <iomanager>
-        <!-- class element defines the manager to be used. The specified class
-             must implement the IOManager interface.
-             Note, that the handlers are being added and called in the order
-             they appear in the configuration.
-        -->
-        <class name="org.apache.jackrabbit.server.io.IOManagerImpl" />
-        <iohandler>
-            <class name="org.apache.jackrabbit.server.io.VersionHandler" />
-        </iohandler>
-        <iohandler>
-            <class name="org.apache.jackrabbit.server.io.VersionHistoryHandler" />
-        </iohandler>
-<!--         <iohandler> -->
-<!--             <class name="org.apache.jackrabbit.server.io.ZipHandler" /> -->
-<!--         </iohandler> -->
-<!--         <iohandler> -->
-<!--             <class name="org.apache.jackrabbit.server.io.XmlHandler" /> -->
-<!--         </iohandler> -->
-        <iohandler>
-            <class name="org.apache.jackrabbit.server.io.DirListingExportHandler" />
-        </iohandler>
-        <iohandler>
-            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
-        </iohandler>
-    </iomanager>
-    <!--
-     Example config for iomanager that populates its list of handlers with
-     default values. Therefore the 'iohandler' elements are omited.
-    -->
-    <!--
-    <iomanager>
-        <class name="org.apache.jackrabbit.server.io.DefaultIOManager" />
-    </iomanager>
-    -->
-    <!--
-     Defines the PropertyManager implementation that is responsible for export
-     and import of resource properties.
-    -->
-    <propertymanager>
-        <!-- class element defines the manager to be used. The specified class
-             must implement the PropertyManager interface.
-             Note, that the handlers are being added and called in the order
-             they appear in the configuration.
-        -->
-        <class name="org.apache.jackrabbit.server.io.PropertyManagerImpl" />
-        <propertyhandler>
-            <class name="org.apache.jackrabbit.server.io.VersionHandler" />
-        </propertyhandler>
-        <propertyhandler>
-            <class name="org.apache.jackrabbit.server.io.VersionHistoryHandler" />
-        </propertyhandler>
-<!--         <propertyhandler> -->
-<!--             <class name="org.apache.jackrabbit.server.io.ZipHandler" /> -->
-<!--         </propertyhandler> -->
-<!--         <propertyhandler> -->
-<!--             <class name="org.apache.jackrabbit.server.io.XmlHandler" /> -->
-<!--         </propertyhandler> -->
-        <propertyhandler>
-            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
-        </propertyhandler>
-    </propertymanager>
-    <!--
-     Define nodetypes, that should never by displayed as 'collection'
-    -->
-    <noncollection>
-        <nodetypes>
-            <nodetype>nt:file</nodetype>
-            <nodetype>nt:resource</nodetype>
-        </nodetypes>
-    </noncollection>
-    <!--
-     Example: Defines nodetypes, that should always be displayed as 'collection'.
-    -->
-    <!--
-    <collection>
-        <nodetypes>
-            <nodetype>nt:folder</nodetype>
-            <nodetype>rep:root</nodetype>
-        </nodetypes>
-    </collection>
-    -->
-    <!--
-     Filter that allows to prevent certain items from being displayed.
-     Please note, that this has an effect on PROPFIND calls only and does not
-     provide limited access to those items matching any of the filters.
-
-     However specifying a filter may cause problems with PUT or MKCOL if the
-     resource to be created is being filtered out, thus resulting in inconsistent
-     responses (e.g. PUT followed by PROPFIND on parent).
-     -->
-    <filter>
-        <!-- class element defines the resource filter to be used. The specified class
-             must implement the ItemFilter interface -->
-        <class name="org.apache.jackrabbit.webdav.simple.DefaultItemFilter" />
-        <!--
-         Nodetype names to be used to filter child nodes.
-         A child node can be filtered if the declaring nodetype of its definition
-         is one of the nodetype names specified in the nodetypes Element.
-         E.g. defining 'rep:root' as filtered nodetype whould result in jcr:system
-         being hidden but no other child node of the root node, since those
-         are defined by the nodetype nt:unstructered.
-        -->
-        <!--
-        <nodetypes>
-            <nodetype>rep:root</nodetype>
-        </nodetypes>
-        -->
-        <!--
-         Namespace prefixes or uris. Items having a name that matches any of the
-         entries will be filtered.
-        -->
-        <namespaces>
-            <prefix>rep</prefix>
-            <prefix>jcr</prefix>
-            <!--
-            <uri>internal</uri>
-            <uri>http://www.jcp.org/jcr/1.0</uri>
-            -->
-        </namespaces>
-    </filter>
-    
-    <!--
-     Optional 'mimetypeproperties' element.
-     It defines additional or replaces existing mappings for the MimeResolver
-     instance created by the ResourceConfig.
-     The default mappings are defined in org.apache.jackrabbit.server.io.mimetypes.properties.
-     If the default mime type defined by MimeResolver is 'application/octet-stream'.
-    -->
-    <!--
-    <mimetypeproperties>
-        <mimemapping extension="rtf" mimetype="application/rtf" />
-        <mimemapping extension="ott" mimetype="application/vnd.oasis.opendocument.text-template" />
-        <defaultmimetype>text/html</defaultmimetype>
-    </mimetypeproperties>
-    -->
-</config>