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
.http
.HttpServletRequest
;
12 import javax
.servlet
.http
.HttpServletResponse
;
13 import javax
.servlet
.http
.HttpSession
;
15 import org
.apache
.commons
.codec
.binary
.Base64
;
16 import org
.apache
.commons
.logging
.Log
;
17 import org
.apache
.commons
.logging
.LogFactory
;
18 import org
.argeo
.cms
.CmsException
;
19 import org
.argeo
.jackrabbit
.servlet
.OpenInViewSessionProvider
;
20 import org
.argeo
.jackrabbit
.servlet
.RemotingServlet
;
21 import org
.argeo
.jackrabbit
.servlet
.WebdavServlet
;
22 import org
.argeo
.jcr
.ArgeoJcrConstants
;
23 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
24 import org
.osgi
.framework
.BundleContext
;
25 import org
.osgi
.service
.http
.NamespaceException
;
26 import org
.osgi
.util
.tracker
.ServiceTracker
;
27 import org
.springframework
.security
.authentication
.AuthenticationManager
;
28 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
29 import org
.springframework
.security
.core
.Authentication
;
30 import org
.springframework
.security
.core
.context
.SecurityContext
;
31 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
34 * Intercepts and enriches http access, mainly focusing on security and
37 class NodeHttp
implements KernelConstants
, ArgeoJcrConstants
{
38 private final static Log log
= LogFactory
.getLog(NodeHttp
.class);
40 private final static String ATTR_AUTH
= "auth";
41 private final static String HEADER_AUTHORIZATION
= "Authorization";
42 private final static String HEADER_WWW_AUTHENTICATE
= "WWW-Authenticate";
44 private final AuthenticationManager authenticationManager
;
45 private final BundleContext bundleContext
;
46 private ExtendedHttpService httpService
;
48 // FIXME Make it more unique
49 private String httpAuthRealm
= "Argeo";
52 private final RootFilter rootFilter
;
53 // private final DoSFilter dosFilter;
54 // private final QoSFilter qosFilter;
57 private OpenInViewSessionProvider sessionProvider
;
58 private WebdavServlet publicWebdavServlet
;
59 private WebdavServlet privateWebdavServlet
;
60 private RemotingServlet publicRemotingServlet
;
61 private RemotingServlet privateRemotingServlet
;
63 NodeHttp(BundleContext bundleContext
, JackrabbitNode node
,
64 NodeSecurity authenticationManager
) {
65 this.bundleContext
= bundleContext
;
66 this.authenticationManager
= authenticationManager
;
69 ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
> st
= new ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
>(
70 bundleContext
, ExtendedHttpService
.class, null);
73 httpService
= st
.waitForService(1000);
74 } catch (InterruptedException e
) {
78 if (httpService
== null)
79 throw new CmsException("Could not find "
80 + ExtendedHttpService
.class + " service.");
83 rootFilter
= new RootFilter();
84 // dosFilter = new CustomDosFilter();
85 // qosFilter = new QoSFilter();
88 sessionProvider
= new OpenInViewSessionProvider();
89 publicWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
90 privateWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
91 publicRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
92 privateRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
97 registerWebdavServlet(PATH_WEBDAV_PUBLIC
, ALIAS_NODE
, true,
99 registerWebdavServlet(PATH_WEBDAV_PRIVATE
, ALIAS_NODE
, false,
100 privateWebdavServlet
);
101 registerRemotingServlet(PATH_REMOTING_PUBLIC
, ALIAS_NODE
, true,
102 publicRemotingServlet
);
103 registerRemotingServlet(PATH_REMOTING_PRIVATE
, ALIAS_NODE
, false,
104 privateRemotingServlet
);
106 // httpService.registerFilter("/", dosFilter, null, null);
107 httpService
.registerFilter("/", rootFilter
, null, null);
108 // httpService.registerFilter("/", qosFilter, null, null);
109 } catch (Exception e
) {
110 throw new CmsException("Cannot publish HTTP services to OSGi", e
);
114 private void registerWebdavServlet(String pathPrefix
, String alias
,
115 Boolean anonymous
, WebdavServlet webdavServlet
)
116 throws NamespaceException
, ServletException
{
117 String path
= pathPrefix
+ "/" + alias
;
118 Properties initParameters
= new Properties();
119 initParameters
.setProperty(WebdavServlet
.INIT_PARAM_RESOURCE_CONFIG
,
120 KernelConstants
.WEBDAV_CONFIG
);
121 initParameters
.setProperty(
122 WebdavServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
123 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
124 : new DavFilter(), null, null);
125 // Cast to servlet because of a weird behaviour in Eclipse
126 httpService
.registerServlet(path
, (Servlet
) webdavServlet
,
127 initParameters
, null);
130 private void registerRemotingServlet(String pathPrefix
, String alias
,
131 Boolean anonymous
, RemotingServlet remotingServlet
)
132 throws NamespaceException
, ServletException
{
133 String path
= pathPrefix
+ "/" + alias
;
134 Properties initParameters
= new Properties();
135 initParameters
.setProperty(
136 RemotingServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
138 // Looks like a bug in Jackrabbit remoting init
139 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_HOME
,
140 KernelUtils
.getOsgiInstanceDir(bundleContext
)
141 + "/tmp/jackrabbit");
142 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_TMP_DIRECTORY
,
144 // Cast to servlet because of a weird behaviour in Eclipse
145 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
146 : new DavFilter(), null, null);
147 httpService
.registerServlet(path
, (Servlet
) remotingServlet
,
148 initParameters
, null);
151 private Boolean
isSessionAuthenticated(HttpSession httpSession
) {
152 SecurityContext contextFromSession
= (SecurityContext
) httpSession
153 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
);
154 return contextFromSession
!= null;
157 private void requestBasicAuth(HttpSession httpSession
,
158 HttpServletResponse response
) {
159 response
.setStatus(401);
160 response
.setHeader(HEADER_WWW_AUTHENTICATE
, "basic realm=\""
161 + httpAuthRealm
+ "\"");
162 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.TRUE
);
165 private UsernamePasswordAuthenticationToken
basicAuth(String authHeader
) {
166 if (authHeader
!= null) {
167 StringTokenizer st
= new StringTokenizer(authHeader
);
168 if (st
.hasMoreTokens()) {
169 String basic
= st
.nextToken();
170 if (basic
.equalsIgnoreCase("Basic")) {
172 String credentials
= new String(Base64
.decodeBase64(st
173 .nextToken()), "UTF-8");
174 // log.debug("Credentials: " + credentials);
175 int p
= credentials
.indexOf(":");
177 String login
= credentials
.substring(0, p
).trim();
178 String password
= credentials
.substring(p
+ 1)
181 return new UsernamePasswordAuthenticationToken(
182 login
, password
.toCharArray());
184 throw new CmsException(
185 "Invalid authentication token");
187 } catch (Exception e
) {
188 throw new CmsException(
189 "Couldn't retrieve authentication", e
);
194 throw new CmsException("Couldn't retrieve authentication");
197 /** Intercepts all requests. Authenticates. */
198 class RootFilter
extends HttpFilter
{
201 public void doFilter(HttpSession httpSession
,
202 HttpServletRequest request
, HttpServletResponse response
,
203 FilterChain filterChain
) throws IOException
, ServletException
{
204 if (log
.isTraceEnabled()) {
205 log
.debug(request
.getContextPath());
206 log
.debug(request
.getServletPath());
207 log
.debug(request
.getRequestURI());
208 log
.debug(request
.getQueryString());
209 StringBuilder buf
= new StringBuilder();
210 Enumeration
<String
> en
= request
.getHeaderNames();
211 while (en
.hasMoreElements()) {
212 String header
= en
.nextElement();
213 Enumeration
<String
> values
= request
.getHeaders(header
);
214 while (values
.hasMoreElements())
215 buf
.append(" " + header
+ ": " + values
.nextElement());
218 log
.debug("\n" + buf
);
221 String servletPath
= request
.getServletPath();
224 if (servletPath
.startsWith(PATH_DATA
)) {
225 filterChain
.doFilter(request
, response
);
229 // skip /ui (workbench) for the time being
230 if (servletPath
.startsWith(PATH_WORKBENCH
)) {
231 filterChain
.doFilter(request
, response
);
235 // redirect long RWT paths to anchor
236 String path
= request
.getRequestURI().substring(
237 servletPath
.length());
238 int pathLength
= path
.length();
239 if (pathLength
!= 0 && (path
.charAt(0) == '/')
240 && !servletPath
.endsWith("rwt-resources")
241 && !path
.equals("/")) {
242 String newLocation
= request
.getServletPath() + "#" + path
;
243 response
.setHeader("Location", newLocation
);
244 response
.setStatus(HttpServletResponse
.SC_FOUND
);
249 filterChain
.doFilter(request
, response
);
253 /** Intercepts all requests. Authenticates. */
254 class AnonymousFilter
extends HttpFilter
{
256 public void doFilter(HttpSession httpSession
,
257 HttpServletRequest request
, HttpServletResponse response
,
258 FilterChain filterChain
) throws IOException
, ServletException
{
260 // Authenticate from session
261 if (isSessionAuthenticated(httpSession
)) {
262 filterChain
.doFilter(request
, response
);
266 KernelUtils
.anonymousLogin(authenticationManager
);
267 filterChain
.doFilter(request
, response
);
271 /** Intercepts all requests. Authenticates. */
272 class DavFilter
extends HttpFilter
{
275 public void doFilter(HttpSession httpSession
,
276 HttpServletRequest request
, HttpServletResponse response
,
277 FilterChain filterChain
) throws IOException
, ServletException
{
279 // Authenticate from session
280 // if (isSessionAuthenticated(httpSession)) {
281 // filterChain.doFilter(request, response);
285 // Process basic auth
286 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
287 if (basicAuth
!= null) {
288 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
289 Authentication auth
= authenticationManager
.authenticate(token
);
290 SecurityContextHolder
.getContext().setAuthentication(auth
);
291 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
,
292 SecurityContextHolder
.getContext());
293 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.FALSE
);
294 filterChain
.doFilter(request
, response
);
298 requestBasicAuth(httpSession
, response
);
302 // class CustomDosFilter extends DoSFilter {
304 // protected String extractUserId(ServletRequest request) {
305 // HttpSession httpSession = ((HttpServletRequest) request)
307 // if (isSessionAuthenticated(httpSession)) {
308 // String userId = ((SecurityContext) httpSession
309 // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
310 // .getAuthentication().getName();
313 // return super.extractUserId(request);