1 package org
.argeo
.cms
.internal
.kernel
;
3 import java
.io
.IOException
;
4 import java
.util
.Properties
;
5 import java
.util
.StringTokenizer
;
7 import javax
.servlet
.FilterChain
;
8 import javax
.servlet
.Servlet
;
9 import javax
.servlet
.ServletException
;
10 import javax
.servlet
.http
.HttpServletRequest
;
11 import javax
.servlet
.http
.HttpServletResponse
;
12 import javax
.servlet
.http
.HttpSession
;
14 import org
.apache
.commons
.codec
.binary
.Base64
;
15 import org
.apache
.commons
.logging
.Log
;
16 import org
.apache
.commons
.logging
.LogFactory
;
17 import org
.argeo
.cms
.CmsException
;
18 import org
.argeo
.cms
.internal
.kernel
.NodeHttp
.AnonymousFilter
;
19 import org
.argeo
.cms
.internal
.kernel
.NodeHttp
.DavFilter
;
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
.osgi
.framework
.BundleContext
;
26 import org
.osgi
.service
.http
.NamespaceException
;
27 import org
.osgi
.util
.tracker
.ServiceTracker
;
28 import org
.springframework
.security
.authentication
.AuthenticationManager
;
29 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
30 import org
.springframework
.security
.core
.Authentication
;
31 import org
.springframework
.security
.core
.context
.SecurityContext
;
32 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
35 * Intercepts and enriches http access, mainly focusing on security and
38 class NodeHttp
implements KernelConstants
, ArgeoJcrConstants
{
39 private final static Log log
= LogFactory
.getLog(NodeHttp
.class);
41 private final static String ATTR_AUTH
= "auth";
42 private final static String HEADER_AUTHORIZATION
= "Authorization";
43 private final static String HEADER_WWW_AUTHENTICATE
= "WWW-Authenticate";
45 static final String SPRING_SECURITY_CONTEXT_KEY
= "SPRING_SECURITY_CONTEXT";
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
;
58 private OpenInViewSessionProvider sessionProvider
;
59 private WebdavServlet publicWebdavServlet
;
60 private WebdavServlet privateWebdavServlet
;
61 private RemotingServlet publicRemotingServlet
;
62 private RemotingServlet privateRemotingServlet
;
64 NodeHttp(BundleContext bundleContext
, JackrabbitNode node
,
65 NodeSecurity authenticationManager
) {
66 this.bundleContext
= bundleContext
;
67 this.authenticationManager
= authenticationManager
;
70 ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
> st
= new ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
>(
71 bundleContext
, ExtendedHttpService
.class, null);
74 httpService
= st
.waitForService(1000);
75 } catch (InterruptedException e
) {
79 if (httpService
== null)
80 throw new CmsException("Could not find "
81 + ExtendedHttpService
.class + " service.");
84 rootFilter
= new RootFilter();
87 sessionProvider
= new OpenInViewSessionProvider();
88 publicWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
89 privateWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
90 publicRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
91 privateRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
96 registerWebdavServlet(PATH_WEBDAV_PUBLIC
, ALIAS_NODE
, true,
98 registerWebdavServlet(PATH_WEBDAV_PRIVATE
, ALIAS_NODE
, false,
99 privateWebdavServlet
);
100 registerRemotingServlet(PATH_REMOTING_PUBLIC
, ALIAS_NODE
, true,
101 publicRemotingServlet
);
102 registerRemotingServlet(PATH_REMOTING_PRIVATE
, ALIAS_NODE
, false,
103 privateRemotingServlet
);
105 // httpService.registerFilter("/", rootFilter, null, null);
106 } catch (Exception e
) {
107 throw new CmsException("Cannot publish HTTP services to OSGi", e
);
111 private void registerWebdavServlet(String pathPrefix
, String alias
,
112 Boolean anonymous
, WebdavServlet webdavServlet
)
113 throws NamespaceException
, ServletException
{
114 String path
= pathPrefix
+ "/" + alias
;
115 Properties initParameters
= new Properties();
116 initParameters
.setProperty(WebdavServlet
.INIT_PARAM_RESOURCE_CONFIG
,
117 KernelConstants
.WEBDAV_CONFIG
);
118 initParameters
.setProperty(
119 WebdavServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
120 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
121 : new DavFilter(), null, null);
122 // Cast to servlet because of a weird behaviour in Eclipse
123 httpService
.registerServlet(path
, (Servlet
) webdavServlet
,
124 initParameters
, null);
127 private void registerRemotingServlet(String pathPrefix
, String alias
,
128 Boolean anonymous
, RemotingServlet remotingServlet
)
129 throws NamespaceException
, ServletException
{
130 String path
= pathPrefix
+ "/" + alias
;
131 Properties initParameters
= new Properties();
132 initParameters
.setProperty(
133 RemotingServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
135 // Looks like a bug in Jackrabbit remoting init
136 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_HOME
,
137 KernelUtils
.getOsgiInstanceDir(bundleContext
)
138 + "/tmp/jackrabbit");
139 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_TMP_DIRECTORY
,
141 // Cast to servlet because of a weird behaviour in Eclipse
142 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
143 : new DavFilter(), null, null);
144 httpService
.registerServlet(path
, (Servlet
) remotingServlet
,
145 initParameters
, null);
148 private Boolean
isSessionAuthenticated(HttpSession httpSession
) {
149 SecurityContext contextFromSession
= (SecurityContext
) httpSession
150 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
);
151 return contextFromSession
!= null;
154 private void requestBasicAuth(HttpSession httpSession
,
155 HttpServletResponse response
) {
156 response
.setStatus(401);
157 response
.setHeader(HEADER_WWW_AUTHENTICATE
, "basic realm=\""
158 + httpAuthRealm
+ "\"");
159 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.TRUE
);
162 private UsernamePasswordAuthenticationToken
basicAuth(String authHeader
) {
163 if (authHeader
!= null) {
164 StringTokenizer st
= new StringTokenizer(authHeader
);
165 if (st
.hasMoreTokens()) {
166 String basic
= st
.nextToken();
167 if (basic
.equalsIgnoreCase("Basic")) {
169 String credentials
= new String(Base64
.decodeBase64(st
170 .nextToken()), "UTF-8");
171 log
.debug("Credentials: " + credentials
);
172 int p
= credentials
.indexOf(":");
174 String login
= credentials
.substring(0, p
).trim();
175 String password
= credentials
.substring(p
+ 1)
178 return new UsernamePasswordAuthenticationToken(
181 throw new CmsException(
182 "Invalid authentication token");
184 } catch (Exception e
) {
185 throw new CmsException(
186 "Couldn't retrieve authentication", e
);
191 throw new CmsException("Couldn't retrieve authentication");
194 /** Intercepts all requests. Authenticates. */
195 class RootFilter
extends HttpFilter
{
198 public void doFilter(HttpSession httpSession
,
199 HttpServletRequest request
, HttpServletResponse response
,
200 FilterChain filterChain
) throws IOException
, ServletException
{
202 // Authenticate from session
203 if (isSessionAuthenticated(httpSession
)) {
204 filterChain
.doFilter(request
, response
);
212 // Process basic auth
213 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
214 if (basicAuth
!= null) {
215 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
216 Authentication auth
= authenticationManager
.authenticate(token
);
217 SecurityContextHolder
.getContext().setAuthentication(auth
);
218 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
,
219 SecurityContextHolder
.getContext());
220 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.FALSE
);
221 filterChain
.doFilter(request
, response
);
225 Boolean doBasicAuth
= true;
227 requestBasicAuth(httpSession
, response
);
235 KernelUtils
.anonymousLogin(authenticationManager
);
236 filterChain
.doFilter(request
, response
);
240 /** Intercepts all requests. Authenticates. */
241 class AnonymousFilter
extends HttpFilter
{
243 public void doFilter(HttpSession httpSession
,
244 HttpServletRequest request
, HttpServletResponse response
,
245 FilterChain filterChain
) throws IOException
, ServletException
{
247 // Authenticate from session
248 if (isSessionAuthenticated(httpSession
)) {
249 filterChain
.doFilter(request
, response
);
253 KernelUtils
.anonymousLogin(authenticationManager
);
254 filterChain
.doFilter(request
, response
);
258 /** Intercepts all requests. Authenticates. */
259 class DavFilter
extends HttpFilter
{
262 public void doFilter(HttpSession httpSession
,
263 HttpServletRequest request
, HttpServletResponse response
,
264 FilterChain filterChain
) throws IOException
, ServletException
{
266 // Authenticate from session
267 if (isSessionAuthenticated(httpSession
)) {
268 filterChain
.doFilter(request
, response
);
272 // Process basic auth
273 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
274 if (basicAuth
!= null) {
275 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
276 Authentication auth
= authenticationManager
.authenticate(token
);
277 SecurityContextHolder
.getContext().setAuthentication(auth
);
278 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
,
279 SecurityContextHolder
.getContext());
280 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.FALSE
);
281 filterChain
.doFilter(request
, response
);
285 requestBasicAuth(httpSession
, response
);