X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fkernel%2FNodeHttp.java;h=f88ba678989f3d7261374c7d79ee9d02f39d3942;hb=1339280e5c79d505b283855b9373488380acd29f;hp=a9142c9334cdc69aaa00a6da487b6be1b18edfb0;hpb=104096c83f55f5afd36e2d4ba578ceed4ed4ae77;p=lgpl%2Fargeo-commons.git 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 index a9142c933..f88ba6789 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java @@ -1,36 +1,21 @@ package org.argeo.cms.internal.kernel; import java.io.IOException; +import java.security.cert.X509Certificate; import java.util.Enumeration; -import java.util.Properties; -import java.util.StringTokenizer; import javax.servlet.FilterChain; -import javax.servlet.Servlet; import javax.servlet.ServletException; -import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -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.jackrabbit.servlet.OpenInViewSessionProvider; -import org.argeo.jackrabbit.servlet.RemotingServlet; -import org.argeo.jackrabbit.servlet.WebdavServlet; import org.argeo.jcr.ArgeoJcrConstants; import org.eclipse.equinox.http.servlet.ExtendedHttpService; -import org.eclipse.jetty.servlets.DoSFilter; -import org.osgi.framework.BundleContext; -import org.osgi.service.http.NamespaceException; -import org.osgi.util.tracker.ServiceTracker; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; /** * Intercepts and enriches http access, mainly focusing on security and @@ -39,161 +24,62 @@ import org.springframework.security.core.context.SecurityContextHolder; class NodeHttp implements KernelConstants, ArgeoJcrConstants { private final static Log log = LogFactory.getLog(NodeHttp.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 AuthenticationManager authenticationManager; - private final BundleContext bundleContext; - private ExtendedHttpService httpService; - - // FIXME Make it more unique - private String httpAuthRealm = "Argeo"; - // Filters - private final RootFilter rootFilter; + // private final RootFilter rootFilter; + // private final DoSFilter dosFilter; // private final QoSFilter qosFilter; - // remoting - private OpenInViewSessionProvider sessionProvider; - private WebdavServlet publicWebdavServlet; - private WebdavServlet privateWebdavServlet; - private RemotingServlet publicRemotingServlet; - private RemotingServlet privateRemotingServlet; - - NodeHttp(BundleContext bundleContext, JackrabbitNode node, - NodeSecurity authenticationManager) { - this.bundleContext = bundleContext; - this.authenticationManager = authenticationManager; - - // Equinox dependency - ServiceTracker st = new ServiceTracker( - bundleContext, ExtendedHttpService.class, null); - st.open(); - try { - httpService = st.waitForService(1000); - } catch (InterruptedException e) { - httpService = null; - } - - if (httpService == null) - throw new CmsException("Could not find " - + ExtendedHttpService.class + " service."); - - // Filters - rootFilter = new RootFilter(); + NodeHttp(ExtendedHttpService httpService) { + // rootFilter = new RootFilter(); // dosFilter = new CustomDosFilter(); // qosFilter = new QoSFilter(); - // DAV - sessionProvider = new OpenInViewSessionProvider(); - publicWebdavServlet = new WebdavServlet(node, sessionProvider); - privateWebdavServlet = new WebdavServlet(node, sessionProvider); - publicRemotingServlet = new RemotingServlet(node, sessionProvider); - privateRemotingServlet = new RemotingServlet(node, sessionProvider); - } - - void publish() { try { - registerWebdavServlet(PATH_WEBDAV_PUBLIC, ALIAS_NODE, true, - publicWebdavServlet); - registerWebdavServlet(PATH_WEBDAV_PRIVATE, ALIAS_NODE, false, - privateWebdavServlet); - registerRemotingServlet(PATH_REMOTING_PUBLIC, ALIAS_NODE, true, - publicRemotingServlet); - registerRemotingServlet(PATH_REMOTING_PRIVATE, ALIAS_NODE, false, - privateRemotingServlet); - - // httpService.registerFilter("/", dosFilter, null, null); - httpService.registerFilter("/", rootFilter, null, null); - // httpService.registerFilter("/", qosFilter, null, null); + httpService.registerServlet("/!", new LinkServlet(), null, null); + // httpService.registerFilter("/", rootFilter, null, null); } catch (Exception e) { - throw new CmsException("Cannot publish HTTP services to OSGi", e); + throw new CmsException("Cannot register filters", e); } } - private void registerWebdavServlet(String pathPrefix, String alias, - Boolean anonymous, WebdavServlet webdavServlet) - throws NamespaceException, ServletException { - String path = pathPrefix + "/" + alias; - Properties initParameters = new Properties(); - initParameters.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, - KernelConstants.WEBDAV_CONFIG); - initParameters.setProperty( - WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); - httpService.registerFilter(path, anonymous ? new AnonymousFilter() - : new DavFilter(), null, null); - // Cast to servlet because of a weird behaviour in Eclipse - httpService.registerServlet(path, (Servlet) webdavServlet, - initParameters, null); + public void destroy() { } - private void registerRemotingServlet(String pathPrefix, String alias, - Boolean anonymous, RemotingServlet remotingServlet) - throws NamespaceException, ServletException { - String path = pathPrefix + "/" + alias; - Properties initParameters = new Properties(); - initParameters.setProperty( - RemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path); - - // Looks like a bug in Jackrabbit remoting init - initParameters.setProperty(RemotingServlet.INIT_PARAM_HOME, - KernelUtils.getOsgiInstanceDir(bundleContext) - + "/tmp/jackrabbit"); - initParameters.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, - "remoting"); - // Cast to servlet because of a weird behaviour in Eclipse - httpService.registerFilter(path, anonymous ? new AnonymousFilter() - : new DavFilter(), null, null); - httpService.registerServlet(path, (Servlet) remotingServlet, - initParameters, null); - } + class LinkServlet extends HttpServlet { + private static final long serialVersionUID = 3749990143146845708L; - private Boolean isSessionAuthenticated(HttpSession httpSession) { - SecurityContext contextFromSession = (SecurityContext) httpSession - .getAttribute(SPRING_SECURITY_CONTEXT_KEY); - return contextFromSession != null; - } - - private void requestBasicAuth(HttpSession httpSession, - HttpServletResponse response) { - response.setStatus(401); - response.setHeader(HEADER_WWW_AUTHENTICATE, "basic realm=\"" - + httpAuthRealm + "\""); - httpSession.setAttribute(ATTR_AUTH, Boolean.TRUE); - } - - private UsernamePasswordAuthenticationToken basicAuth(String authHeader) { - if (authHeader != null) { - StringTokenizer st = new StringTokenizer(authHeader); - if (st.hasMoreTokens()) { - String basic = st.nextToken(); - if (basic.equalsIgnoreCase("Basic")) { - try { - String credentials = new String(Base64.decodeBase64(st - .nextToken()), "UTF-8"); - // log.debug("Credentials: " + credentials); - int p = credentials.indexOf(":"); - if (p != -1) { - String login = credentials.substring(0, p).trim(); - String password = credentials.substring(p + 1) - .trim(); - - return new UsernamePasswordAuthenticationToken( - login, password.toCharArray()); - } else { - throw new CmsException( - "Invalid authentication token"); - } - } catch (Exception e) { - throw new CmsException( - "Couldn't retrieve authentication", e); - } - } + @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")); + if (isCompatibleBrowser && log.isTraceEnabled()) + log.trace("# BWS " + request.getHeader("User-Agent")); + // if (isCompatibleBrowser) {// redirect + response.setHeader("Location", "/#" + path); + response.setStatus(HttpServletResponse.SC_FOUND); + // } } - throw new CmsException("Couldn't retrieve authentication"); } /** Intercepts all requests. Authenticates. */ @@ -204,35 +90,42 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants { HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { if (log.isTraceEnabled()) { - log.debug(request.getContextPath()); - log.debug(request.getServletPath()); - log.debug(request.getRequestURI()); - log.debug(request.getQueryString()); - StringBuilder buf = new StringBuilder(); - Enumeration en = request.getHeaderNames(); - while (en.hasMoreElements()) { - String header = en.nextElement(); - Enumeration values = request.getHeaders(header); - while (values.hasMoreElements()) - buf.append(" " + header + ": " + values.nextElement()); - buf.append('\n'); - } - log.debug("\n" + buf); + 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(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()).trim(); - if (!servletPath.endsWith("rwt-resources") && !path.equals("") - && !path.equals("/")) { + 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); @@ -244,68 +137,55 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants { } } - /** Intercepts all requests. Authenticates. */ - class AnonymousFilter extends HttpFilter { - @Override - public void doFilter(HttpSession httpSession, - HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { - - // Authenticate from session - if (isSessionAuthenticated(httpSession)) { - filterChain.doFilter(request, response); - return; - } - - KernelUtils.anonymousLogin(authenticationManager); - 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 en = request.getHeaderNames(); + while (en.hasMoreElements()) { + String header = en.nextElement(); + Enumeration values = request.getHeaders(header); + while (values.hasMoreElements()) + buf.append(" " + header + ": " + values.nextElement()); + buf.append('\n'); } - } - /** Intercepts all requests. Authenticates. */ - class DavFilter extends HttpFilter { - - @Override - public void doFilter(HttpSession httpSession, - HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { - - // Authenticate from session - // if (isSessionAuthenticated(httpSession)) { - // filterChain.doFilter(request, response); - // return; - // } - - // Process basic auth - String basicAuth = request.getHeader(HEADER_AUTHORIZATION); - if (basicAuth != null) { - UsernamePasswordAuthenticationToken token = basicAuth(basicAuth); - Authentication auth = authenticationManager.authenticate(token); - SecurityContextHolder.getContext().setAuthentication(auth); - httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, - SecurityContextHolder.getContext()); - httpSession.setAttribute(ATTR_AUTH, Boolean.FALSE); - filterChain.doFilter(request, response); - return; - } - - requestBasicAuth(httpSession, response); + // attributed + Enumeration 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); } - 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); - + 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); + // + // } + // } }