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
.osgi
.framework
.BundleContext
;
27 import org
.osgi
.service
.http
.NamespaceException
;
28 import org
.osgi
.util
.tracker
.ServiceTracker
;
29 import org
.springframework
.security
.authentication
.AuthenticationManager
;
30 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
31 import org
.springframework
.security
.core
.Authentication
;
32 import org
.springframework
.security
.core
.context
.SecurityContext
;
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 BundleContext bundleContext
;
48 private ExtendedHttpService httpService
;
50 // FIXME Make it more unique
51 private String httpAuthRealm
= "Argeo";
54 private final RootFilter rootFilter
;
55 // private final DoSFilter dosFilter;
56 // private final QoSFilter qosFilter;
59 private OpenInViewSessionProvider sessionProvider
;
60 private WebdavServlet publicWebdavServlet
;
61 private WebdavServlet privateWebdavServlet
;
62 private RemotingServlet publicRemotingServlet
;
63 private RemotingServlet privateRemotingServlet
;
65 NodeHttp(BundleContext bundleContext
, JackrabbitNode node
,
66 NodeSecurity authenticationManager
) {
67 this.bundleContext
= bundleContext
;
68 this.authenticationManager
= authenticationManager
;
71 ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
> st
= new ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
>(
72 bundleContext
, ExtendedHttpService
.class, null);
75 httpService
= st
.waitForService(1000);
76 } catch (InterruptedException e
) {
80 if (httpService
== null)
81 throw new CmsException("Could not find "
82 + ExtendedHttpService
.class + " service.");
85 rootFilter
= new RootFilter();
86 // dosFilter = new CustomDosFilter();
87 // qosFilter = new QoSFilter();
90 sessionProvider
= new OpenInViewSessionProvider();
91 publicWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
92 privateWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
93 publicRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
94 privateRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
99 registerWebdavServlet(PATH_WEBDAV_PUBLIC
, ALIAS_NODE
, true,
100 publicWebdavServlet
);
101 registerWebdavServlet(PATH_WEBDAV_PRIVATE
, ALIAS_NODE
, false,
102 privateWebdavServlet
);
103 registerRemotingServlet(PATH_REMOTING_PUBLIC
, ALIAS_NODE
, true,
104 publicRemotingServlet
);
105 registerRemotingServlet(PATH_REMOTING_PRIVATE
, ALIAS_NODE
, false,
106 privateRemotingServlet
);
108 // httpService.registerFilter("/", dosFilter, null, null);
109 httpService
.registerFilter("/", rootFilter
, null, null);
110 // httpService.registerFilter("/", qosFilter, null, null);
111 } catch (Exception e
) {
112 throw new CmsException("Cannot publish HTTP services to OSGi", e
);
116 private void registerWebdavServlet(String pathPrefix
, String alias
,
117 Boolean anonymous
, WebdavServlet webdavServlet
)
118 throws NamespaceException
, ServletException
{
119 String path
= pathPrefix
+ "/" + alias
;
120 Properties initParameters
= new Properties();
121 initParameters
.setProperty(WebdavServlet
.INIT_PARAM_RESOURCE_CONFIG
,
122 KernelConstants
.WEBDAV_CONFIG
);
123 initParameters
.setProperty(
124 WebdavServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
125 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
126 : new DavFilter(), null, null);
127 // Cast to servlet because of a weird behaviour in Eclipse
128 httpService
.registerServlet(path
, (Servlet
) webdavServlet
,
129 initParameters
, null);
132 private void registerRemotingServlet(String pathPrefix
, String alias
,
133 Boolean anonymous
, RemotingServlet remotingServlet
)
134 throws NamespaceException
, ServletException
{
135 String path
= pathPrefix
+ "/" + alias
;
136 Properties initParameters
= new Properties();
137 initParameters
.setProperty(
138 RemotingServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
140 // Looks like a bug in Jackrabbit remoting init
141 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_HOME
,
142 KernelUtils
.getOsgiInstanceDir(bundleContext
)
143 + "/tmp/jackrabbit");
144 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_TMP_DIRECTORY
,
146 // Cast to servlet because of a weird behaviour in Eclipse
147 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
148 : new DavFilter(), null, null);
149 httpService
.registerServlet(path
, (Servlet
) remotingServlet
,
150 initParameters
, null);
153 private Boolean
isSessionAuthenticated(HttpSession httpSession
) {
154 SecurityContext contextFromSession
= (SecurityContext
) httpSession
155 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
);
156 return contextFromSession
!= null;
159 private void requestBasicAuth(HttpSession httpSession
,
160 HttpServletResponse response
) {
161 response
.setStatus(401);
162 response
.setHeader(HEADER_WWW_AUTHENTICATE
, "basic realm=\""
163 + httpAuthRealm
+ "\"");
164 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.TRUE
);
167 private UsernamePasswordAuthenticationToken
basicAuth(String authHeader
) {
168 if (authHeader
!= null) {
169 StringTokenizer st
= new StringTokenizer(authHeader
);
170 if (st
.hasMoreTokens()) {
171 String basic
= st
.nextToken();
172 if (basic
.equalsIgnoreCase("Basic")) {
174 String credentials
= new String(Base64
.decodeBase64(st
175 .nextToken()), "UTF-8");
176 // log.debug("Credentials: " + credentials);
177 int p
= credentials
.indexOf(":");
179 String login
= credentials
.substring(0, p
).trim();
180 String password
= credentials
.substring(p
+ 1)
183 return new UsernamePasswordAuthenticationToken(
184 login
, password
.toCharArray());
186 throw new CmsException(
187 "Invalid authentication token");
189 } catch (Exception e
) {
190 throw new CmsException(
191 "Couldn't retrieve authentication", e
);
196 throw new CmsException("Couldn't retrieve authentication");
199 /** Intercepts all requests. Authenticates. */
200 class RootFilter
extends HttpFilter
{
203 public void doFilter(HttpSession httpSession
,
204 HttpServletRequest request
, HttpServletResponse response
,
205 FilterChain filterChain
) throws IOException
, ServletException
{
206 if (log
.isTraceEnabled()) {
207 log
.debug(request
.getContextPath());
208 log
.debug(request
.getServletPath());
209 log
.debug(request
.getRequestURI());
210 log
.debug(request
.getQueryString());
211 StringBuilder buf
= new StringBuilder();
212 Enumeration
<String
> en
= request
.getHeaderNames();
213 while (en
.hasMoreElements()) {
214 String header
= en
.nextElement();
215 Enumeration
<String
> values
= request
.getHeaders(header
);
216 while (values
.hasMoreElements())
217 buf
.append(" " + header
+ ": " + values
.nextElement());
220 log
.debug("\n" + buf
);
223 String servletPath
= request
.getServletPath();
226 if (servletPath
.startsWith(PATH_DATA
)) {
227 filterChain
.doFilter(request
, response
);
231 // redirect long RWT paths to anchor
232 String path
= request
.getRequestURI().substring(
233 servletPath
.length());
234 int pathLength
= path
.length();
235 if (pathLength
!= 0 && (path
.charAt(0) == '/')
236 && !servletPath
.endsWith("rwt-resources")
237 && !path
.equals("/")) {
238 String newLocation
= request
.getServletPath() + "#" + path
;
239 response
.setHeader("Location", newLocation
);
240 response
.setStatus(HttpServletResponse
.SC_FOUND
);
245 filterChain
.doFilter(request
, response
);
249 /** Intercepts all requests. Authenticates. */
250 class AnonymousFilter
extends HttpFilter
{
252 public void doFilter(HttpSession httpSession
,
253 HttpServletRequest request
, HttpServletResponse response
,
254 FilterChain filterChain
) throws IOException
, ServletException
{
256 // Authenticate from session
257 if (isSessionAuthenticated(httpSession
)) {
258 filterChain
.doFilter(request
, response
);
262 KernelUtils
.anonymousLogin(authenticationManager
);
263 filterChain
.doFilter(request
, response
);
267 /** Intercepts all requests. Authenticates. */
268 class DavFilter
extends HttpFilter
{
271 public void doFilter(HttpSession httpSession
,
272 HttpServletRequest request
, HttpServletResponse response
,
273 FilterChain filterChain
) throws IOException
, ServletException
{
275 // Authenticate from session
276 // if (isSessionAuthenticated(httpSession)) {
277 // filterChain.doFilter(request, response);
281 // Process basic auth
282 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
283 if (basicAuth
!= null) {
284 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
285 Authentication auth
= authenticationManager
.authenticate(token
);
286 SecurityContextHolder
.getContext().setAuthentication(auth
);
287 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
,
288 SecurityContextHolder
.getContext());
289 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.FALSE
);
290 filterChain
.doFilter(request
, response
);
294 requestBasicAuth(httpSession
, response
);
298 class CustomDosFilter
extends DoSFilter
{
300 protected String
extractUserId(ServletRequest request
) {
301 HttpSession httpSession
= ((HttpServletRequest
) request
)
303 if (isSessionAuthenticated(httpSession
)) {
304 String userId
= ((SecurityContext
) httpSession
305 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
))
306 .getAuthentication().getName();
309 return super.extractUserId(request
);