1 package org
.argeo
.cms
.internal
.kernel
;
3 import java
.io
.IOException
;
4 import java
.util
.Enumeration
;
5 import java
.util
.Properties
;
6 import java
.util
.StringTokenizer
;
8 import javax
.servlet
.FilterChain
;
9 import javax
.servlet
.Servlet
;
10 import javax
.servlet
.ServletException
;
11 import javax
.servlet
.ServletRequest
;
12 import javax
.servlet
.http
.HttpServletRequest
;
13 import javax
.servlet
.http
.HttpServletResponse
;
14 import javax
.servlet
.http
.HttpSession
;
16 import org
.apache
.commons
.codec
.binary
.Base64
;
17 import org
.apache
.commons
.logging
.Log
;
18 import org
.apache
.commons
.logging
.LogFactory
;
19 import org
.argeo
.cms
.CmsException
;
20 import org
.argeo
.jackrabbit
.servlet
.OpenInViewSessionProvider
;
21 import org
.argeo
.jackrabbit
.servlet
.RemotingServlet
;
22 import org
.argeo
.jackrabbit
.servlet
.WebdavServlet
;
23 import org
.argeo
.jcr
.ArgeoJcrConstants
;
24 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
25 import org
.eclipse
.jetty
.servlets
.DoSFilter
;
26 import org
.eclipse
.jetty
.servlets
.QoSFilter
;
27 import org
.osgi
.framework
.BundleContext
;
28 import org
.osgi
.service
.http
.NamespaceException
;
29 import org
.osgi
.util
.tracker
.ServiceTracker
;
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
.SecurityContext
;
34 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
37 * Intercepts and enriches http access, mainly focusing on security and
40 class NodeHttp
implements KernelConstants
, ArgeoJcrConstants
{
41 private final static Log log
= LogFactory
.getLog(NodeHttp
.class);
43 private final static String ATTR_AUTH
= "auth";
44 private final static String HEADER_AUTHORIZATION
= "Authorization";
45 private final static String HEADER_WWW_AUTHENTICATE
= "WWW-Authenticate";
47 private final AuthenticationManager authenticationManager
;
48 private final BundleContext bundleContext
;
49 private ExtendedHttpService httpService
;
51 // FIXME Make it more unique
52 private String httpAuthRealm
= "Argeo";
55 private final RootFilter rootFilter
;
56 private final DoSFilter dosFilter
;
57 private final QoSFilter qosFilter
;
60 private OpenInViewSessionProvider sessionProvider
;
61 private WebdavServlet publicWebdavServlet
;
62 private WebdavServlet privateWebdavServlet
;
63 private RemotingServlet publicRemotingServlet
;
64 private RemotingServlet privateRemotingServlet
;
66 NodeHttp(BundleContext bundleContext
, JackrabbitNode node
,
67 NodeSecurity authenticationManager
) {
68 this.bundleContext
= bundleContext
;
69 this.authenticationManager
= authenticationManager
;
72 ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
> st
= new ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
>(
73 bundleContext
, ExtendedHttpService
.class, null);
76 httpService
= st
.waitForService(1000);
77 } catch (InterruptedException e
) {
81 if (httpService
== null)
82 throw new CmsException("Could not find "
83 + ExtendedHttpService
.class + " service.");
86 rootFilter
= new RootFilter();
87 dosFilter
= new CustomDosFilter();
88 qosFilter
= new QoSFilter();
91 sessionProvider
= new OpenInViewSessionProvider();
92 publicWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
93 privateWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
94 publicRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
95 privateRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
100 registerWebdavServlet(PATH_WEBDAV_PUBLIC
, ALIAS_NODE
, true,
101 publicWebdavServlet
);
102 registerWebdavServlet(PATH_WEBDAV_PRIVATE
, ALIAS_NODE
, false,
103 privateWebdavServlet
);
104 registerRemotingServlet(PATH_REMOTING_PUBLIC
, ALIAS_NODE
, true,
105 publicRemotingServlet
);
106 registerRemotingServlet(PATH_REMOTING_PRIVATE
, ALIAS_NODE
, false,
107 privateRemotingServlet
);
109 httpService
.registerFilter("/", dosFilter
, null, null);
110 httpService
.registerFilter("/", rootFilter
, null, null);
111 httpService
.registerFilter("/", qosFilter
, null, null);
112 } catch (Exception e
) {
113 throw new CmsException("Cannot publish HTTP services to OSGi", e
);
117 private void registerWebdavServlet(String pathPrefix
, String alias
,
118 Boolean anonymous
, WebdavServlet webdavServlet
)
119 throws NamespaceException
, ServletException
{
120 String path
= pathPrefix
+ "/" + alias
;
121 Properties initParameters
= new Properties();
122 initParameters
.setProperty(WebdavServlet
.INIT_PARAM_RESOURCE_CONFIG
,
123 KernelConstants
.WEBDAV_CONFIG
);
124 initParameters
.setProperty(
125 WebdavServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
126 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
127 : new DavFilter(), null, null);
128 // Cast to servlet because of a weird behaviour in Eclipse
129 httpService
.registerServlet(path
, (Servlet
) webdavServlet
,
130 initParameters
, null);
133 private void registerRemotingServlet(String pathPrefix
, String alias
,
134 Boolean anonymous
, RemotingServlet remotingServlet
)
135 throws NamespaceException
, ServletException
{
136 String path
= pathPrefix
+ "/" + alias
;
137 Properties initParameters
= new Properties();
138 initParameters
.setProperty(
139 RemotingServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
141 // Looks like a bug in Jackrabbit remoting init
142 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_HOME
,
143 KernelUtils
.getOsgiInstanceDir(bundleContext
)
144 + "/tmp/jackrabbit");
145 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_TMP_DIRECTORY
,
147 // Cast to servlet because of a weird behaviour in Eclipse
148 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
149 : new DavFilter(), null, null);
150 httpService
.registerServlet(path
, (Servlet
) remotingServlet
,
151 initParameters
, null);
154 private Boolean
isSessionAuthenticated(HttpSession httpSession
) {
155 SecurityContext contextFromSession
= (SecurityContext
) httpSession
156 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
);
157 return contextFromSession
!= null;
160 private void requestBasicAuth(HttpSession httpSession
,
161 HttpServletResponse response
) {
162 response
.setStatus(401);
163 response
.setHeader(HEADER_WWW_AUTHENTICATE
, "basic realm=\""
164 + httpAuthRealm
+ "\"");
165 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.TRUE
);
168 private UsernamePasswordAuthenticationToken
basicAuth(String authHeader
) {
169 if (authHeader
!= null) {
170 StringTokenizer st
= new StringTokenizer(authHeader
);
171 if (st
.hasMoreTokens()) {
172 String basic
= st
.nextToken();
173 if (basic
.equalsIgnoreCase("Basic")) {
175 String credentials
= new String(Base64
.decodeBase64(st
176 .nextToken()), "UTF-8");
177 // log.debug("Credentials: " + credentials);
178 int p
= credentials
.indexOf(":");
180 String login
= credentials
.substring(0, p
).trim();
181 String password
= credentials
.substring(p
+ 1)
184 return new UsernamePasswordAuthenticationToken(
185 login
, password
.toCharArray());
187 throw new CmsException(
188 "Invalid authentication token");
190 } catch (Exception e
) {
191 throw new CmsException(
192 "Couldn't retrieve authentication", e
);
197 throw new CmsException("Couldn't retrieve authentication");
200 /** Intercepts all requests. Authenticates. */
201 class RootFilter
extends HttpFilter
{
204 public void doFilter(HttpSession httpSession
,
205 HttpServletRequest request
, HttpServletResponse response
,
206 FilterChain filterChain
) throws IOException
, ServletException
{
207 if (log
.isTraceEnabled()) {
208 log
.debug(request
.getContextPath());
209 log
.debug(request
.getServletPath());
210 log
.debug(request
.getRequestURI());
211 log
.debug(request
.getQueryString());
212 StringBuilder buf
= new StringBuilder();
213 Enumeration
<String
> en
= request
.getHeaderNames();
214 while (en
.hasMoreElements()) {
215 String header
= en
.nextElement();
216 Enumeration
<String
> values
= request
.getHeaders(header
);
217 while (values
.hasMoreElements())
218 buf
.append(" " + header
+ ": " + values
.nextElement());
221 log
.debug("\n" + buf
);
224 String servletPath
= request
.getServletPath();
227 if (servletPath
.startsWith(PATH_DATA
)) {
228 filterChain
.doFilter(request
, response
);
232 // redirect long RWT paths to anchor
233 String path
= request
.getRequestURI()
234 .substring(servletPath
.length()).trim();
235 if (!servletPath
.endsWith("rwt-resources") && !path
.equals("")
236 && !path
.equals("/")) {
237 String newLocation
= request
.getServletPath() + "#" + path
;
238 response
.setHeader("Location", newLocation
);
239 response
.setStatus(HttpServletResponse
.SC_FOUND
);
244 filterChain
.doFilter(request
, response
);
248 /** Intercepts all requests. Authenticates. */
249 class AnonymousFilter
extends HttpFilter
{
251 public void doFilter(HttpSession httpSession
,
252 HttpServletRequest request
, HttpServletResponse response
,
253 FilterChain filterChain
) throws IOException
, ServletException
{
255 // Authenticate from session
256 if (isSessionAuthenticated(httpSession
)) {
257 filterChain
.doFilter(request
, response
);
261 KernelUtils
.anonymousLogin(authenticationManager
);
262 filterChain
.doFilter(request
, response
);
266 /** Intercepts all requests. Authenticates. */
267 class DavFilter
extends HttpFilter
{
270 public void doFilter(HttpSession httpSession
,
271 HttpServletRequest request
, HttpServletResponse response
,
272 FilterChain filterChain
) throws IOException
, ServletException
{
274 // Authenticate from session
275 // if (isSessionAuthenticated(httpSession)) {
276 // filterChain.doFilter(request, response);
280 // Process basic auth
281 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
282 if (basicAuth
!= null) {
283 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
284 Authentication auth
= authenticationManager
.authenticate(token
);
285 SecurityContextHolder
.getContext().setAuthentication(auth
);
286 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
,
287 SecurityContextHolder
.getContext());
288 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.FALSE
);
289 filterChain
.doFilter(request
, response
);
293 requestBasicAuth(httpSession
, response
);
297 class CustomDosFilter
extends DoSFilter
{
299 protected String
extractUserId(ServletRequest request
) {
300 HttpSession httpSession
= ((HttpServletRequest
) request
)
302 if (isSessionAuthenticated(httpSession
)) {
303 String userId
= ((SecurityContext
) httpSession
304 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
))
305 .getAuthentication().getName();
308 return super.extractUserId(request
);