1 package org
.argeo
.cms
.internal
.kernel
;
3 import static org
.argeo
.jackrabbit
.servlet
.WebdavServlet
.INIT_PARAM_RESOURCE_CONFIG
;
5 import java
.io
.IOException
;
6 import java
.security
.cert
.X509Certificate
;
7 import java
.util
.Enumeration
;
8 import java
.util
.Properties
;
9 import java
.util
.StringTokenizer
;
11 import javax
.jcr
.Repository
;
12 import javax
.servlet
.FilterChain
;
13 import javax
.servlet
.Servlet
;
14 import javax
.servlet
.ServletException
;
15 import javax
.servlet
.http
.HttpServletRequest
;
16 import javax
.servlet
.http
.HttpServletResponse
;
17 import javax
.servlet
.http
.HttpSession
;
19 import org
.apache
.commons
.codec
.binary
.Base64
;
20 import org
.apache
.commons
.logging
.Log
;
21 import org
.apache
.commons
.logging
.LogFactory
;
22 import org
.argeo
.cms
.CmsException
;
23 import org
.argeo
.jackrabbit
.servlet
.OpenInViewSessionProvider
;
24 import org
.argeo
.jackrabbit
.servlet
.RemotingServlet
;
25 import org
.argeo
.jackrabbit
.servlet
.WebdavServlet
;
26 import org
.argeo
.jcr
.ArgeoJcrConstants
;
27 import org
.argeo
.security
.NodeAuthenticationToken
;
28 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
29 import org
.osgi
.service
.http
.NamespaceException
;
30 import org
.springframework
.security
.authentication
.AuthenticationManager
;
31 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
32 import org
.springframework
.security
.core
.Authentication
;
33 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
36 * Intercepts and enriches http access, mainly focusing on security and
39 class NodeHttp
implements KernelConstants
, ArgeoJcrConstants
{
40 private final static Log log
= LogFactory
.getLog(NodeHttp
.class);
42 private final static String ATTR_AUTH
= "auth";
43 private final static String HEADER_AUTHORIZATION
= "Authorization";
44 private final static String HEADER_WWW_AUTHENTICATE
= "WWW-Authenticate";
46 private final AuthenticationManager authenticationManager
;
47 private final ExtendedHttpService httpService
;
49 // FIXME Make it more unique
50 private String httpAuthRealm
= "Argeo";
53 private final RootFilter rootFilter
;
54 // private final DoSFilter dosFilter;
55 // private final QoSFilter qosFilter;
57 // WebDav / JCR remoting
58 private OpenInViewSessionProvider sessionProvider
;
60 NodeHttp(ExtendedHttpService httpService
, JackrabbitNode node
,
61 NodeSecurity authenticationManager
) {
62 // this.bundleContext = bundleContext;
63 this.authenticationManager
= authenticationManager
;
65 this.httpService
= httpService
;
68 rootFilter
= new RootFilter();
69 // dosFilter = new CustomDosFilter();
70 // qosFilter = new QoSFilter();
73 sessionProvider
= new OpenInViewSessionProvider();
75 registerRepositoryServlets(ALIAS_NODE
, node
);
77 httpService
.registerFilter("/", rootFilter
, null, null);
78 } catch (Exception e
) {
79 throw new CmsException("Could not register root filter", e
);
83 public void destroy() {
84 sessionProvider
.destroy();
85 unregisterRepositoryServlets(ALIAS_NODE
);
88 void registerRepositoryServlets(String alias
, Repository repository
) {
90 registerWebdavServlet(alias
, repository
, true);
91 registerWebdavServlet(alias
, repository
, false);
92 registerRemotingServlet(alias
, repository
, true);
93 registerRemotingServlet(alias
, repository
, false);
94 } catch (Exception e
) {
95 throw new CmsException(
96 "Could not register servlets for repository " + alias
, e
);
100 void unregisterRepositoryServlets(String alias
) {
101 // FIXME unregister servlets
104 void registerWebdavServlet(String alias
, Repository repository
,
105 boolean anonymous
) throws NamespaceException
, ServletException
{
106 WebdavServlet webdavServlet
= new WebdavServlet(repository
,
108 String pathPrefix
= anonymous ? WEBDAV_PUBLIC
: WEBDAV_PRIVATE
;
109 String path
= pathPrefix
+ "/" + alias
;
110 Properties ip
= new Properties();
111 ip
.setProperty(INIT_PARAM_RESOURCE_CONFIG
, WEBDAV_CONFIG
);
112 ip
.setProperty(WebdavServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
113 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
114 : new DavFilter(), null, null);
115 // Cast to servlet because of a weird behaviour in Eclipse
116 httpService
.registerServlet(path
, (Servlet
) webdavServlet
, ip
, null);
119 void registerRemotingServlet(String alias
, Repository repository
,
120 boolean anonymous
) throws NamespaceException
, ServletException
{
121 String pathPrefix
= anonymous ? REMOTING_PUBLIC
: REMOTING_PRIVATE
;
122 RemotingServlet remotingServlet
= new RemotingServlet(repository
,
124 String path
= pathPrefix
+ "/" + alias
;
125 Properties ip
= new Properties();
126 ip
.setProperty(RemotingServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
128 // Looks like a bug in Jackrabbit remoting init
129 ip
.setProperty(RemotingServlet
.INIT_PARAM_HOME
,
130 KernelUtils
.getOsgiInstanceDir() + "/tmp/jackrabbit");
131 ip
.setProperty(RemotingServlet
.INIT_PARAM_TMP_DIRECTORY
, "remoting");
132 // Cast to servlet because of a weird behaviour in Eclipse
133 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
134 : new DavFilter(), null, null);
135 httpService
.registerServlet(path
, (Servlet
) remotingServlet
, ip
, null);
138 // private Boolean isSessionAuthenticated(HttpSession httpSession) {
139 // SecurityContext contextFromSession = (SecurityContext) httpSession
140 // .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
141 // return contextFromSession != null;
144 private void requestBasicAuth(HttpSession httpSession
,
145 HttpServletResponse response
) {
146 response
.setStatus(401);
147 response
.setHeader(HEADER_WWW_AUTHENTICATE
, "basic realm=\""
148 + httpAuthRealm
+ "\"");
149 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.TRUE
);
152 private NodeAuthenticationToken
basicAuth(String authHeader
) {
153 if (authHeader
!= null) {
154 StringTokenizer st
= new StringTokenizer(authHeader
);
155 if (st
.hasMoreTokens()) {
156 String basic
= st
.nextToken();
157 if (basic
.equalsIgnoreCase("Basic")) {
159 String credentials
= new String(Base64
.decodeBase64(st
160 .nextToken()), "UTF-8");
161 // log.debug("Credentials: " + credentials);
162 int p
= credentials
.indexOf(":");
164 String login
= credentials
.substring(0, p
).trim();
165 String password
= credentials
.substring(p
+ 1)
168 return new NodeAuthenticationToken(login
,
169 password
.toCharArray());
171 throw new CmsException(
172 "Invalid authentication token");
174 } catch (Exception e
) {
175 throw new CmsException(
176 "Couldn't retrieve authentication", e
);
181 throw new CmsException("Couldn't retrieve authentication");
184 /** Intercepts all requests. Authenticates. */
185 class RootFilter
extends HttpFilter
{
188 public void doFilter(HttpSession httpSession
,
189 HttpServletRequest request
, HttpServletResponse response
,
190 FilterChain filterChain
) throws IOException
, ServletException
{
191 if (log
.isTraceEnabled()) {
192 log
.trace(request
.getRequestURL().append(
193 request
.getQueryString() != null ?
"?"
194 + request
.getQueryString() : ""));
198 String servletPath
= request
.getServletPath();
200 // client certificate
201 X509Certificate clientCert
= extractCertificate(request
);
202 if (clientCert
!= null) {
204 // if (log.isDebugEnabled())
205 // log.debug(clientCert.getSubjectX500Principal().getName());
209 if (servletPath
.startsWith(PATH_DATA
)) {
210 filterChain
.doFilter(request
, response
);
214 // skip /ui (workbench) for the time being
215 if (servletPath
.startsWith(PATH_WORKBENCH
)) {
216 filterChain
.doFilter(request
, response
);
220 // redirect long RWT paths to anchor
221 String path
= request
.getRequestURI().substring(
222 servletPath
.length());
223 int pathLength
= path
.length();
224 if (pathLength
!= 0 && (path
.charAt(0) == '/')
225 && !servletPath
.endsWith("rwt-resources")
226 && path
.lastIndexOf('/') != 0) {
227 String newLocation
= request
.getServletPath() + "#" + path
;
228 response
.setHeader("Location", newLocation
);
229 response
.setStatus(HttpServletResponse
.SC_FOUND
);
234 filterChain
.doFilter(request
, response
);
238 private void logRequest(HttpServletRequest request
) {
239 log
.debug("contextPath=" + request
.getContextPath());
240 log
.debug("servletPath=" + request
.getServletPath());
241 log
.debug("requestURI=" + request
.getRequestURI());
242 log
.debug("queryString=" + request
.getQueryString());
243 StringBuilder buf
= new StringBuilder();
245 Enumeration
<String
> en
= request
.getHeaderNames();
246 while (en
.hasMoreElements()) {
247 String header
= en
.nextElement();
248 Enumeration
<String
> values
= request
.getHeaders(header
);
249 while (values
.hasMoreElements())
250 buf
.append(" " + header
+ ": " + values
.nextElement());
255 Enumeration
<String
> an
= request
.getAttributeNames();
256 while (an
.hasMoreElements()) {
257 String attr
= an
.nextElement();
258 Object value
= request
.getAttribute(attr
);
259 buf
.append(" " + attr
+ ": " + value
);
262 log
.debug("\n" + buf
);
265 private X509Certificate
extractCertificate(HttpServletRequest req
) {
266 X509Certificate
[] certs
= (X509Certificate
[]) req
267 .getAttribute("javax.servlet.request.X509Certificate");
268 if (null != certs
&& certs
.length
> 0) {
274 /** Intercepts all requests. Authenticates. */
275 private class AnonymousFilter
extends HttpFilter
{
277 public void doFilter(HttpSession httpSession
,
278 HttpServletRequest request
, HttpServletResponse response
,
279 FilterChain filterChain
) throws IOException
, ServletException
{
281 // Authenticate from session
282 // if (isSessionAuthenticated(httpSession)) {
283 // filterChain.doFilter(request, response);
287 KernelUtils
.anonymousLogin(authenticationManager
);
288 filterChain
.doFilter(request
, response
);
292 /** Intercepts all requests. Authenticates. */
293 private class DavFilter
extends HttpFilter
{
296 public void doFilter(HttpSession httpSession
,
297 HttpServletRequest request
, HttpServletResponse response
,
298 FilterChain filterChain
) throws IOException
, ServletException
{
300 // Authenticate from session
301 // if (isSessionAuthenticated(httpSession)) {
302 // filterChain.doFilter(request, response);
306 // Process basic auth
307 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
308 if (basicAuth
!= null) {
309 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
310 Authentication auth
= authenticationManager
.authenticate(token
);
311 SecurityContextHolder
.getContext().setAuthentication(auth
);
312 // httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,
313 // SecurityContextHolder.getContext());
314 // httpSession.setAttribute(ATTR_AUTH, Boolean.FALSE);
315 filterChain
.doFilter(request
, response
);
319 requestBasicAuth(httpSession
, response
);
323 // class CustomDosFilter extends DoSFilter {
325 // protected String extractUserId(ServletRequest request) {
326 // HttpSession httpSession = ((HttpServletRequest) request)
328 // if (isSessionAuthenticated(httpSession)) {
329 // String userId = ((SecurityContext) httpSession
330 // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
331 // .getAuthentication().getName();
334 // return super.extractUserId(request);