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
.jackrabbit
.servlet
.OpenInViewSessionProvider
;
19 import org
.argeo
.jackrabbit
.servlet
.RemotingServlet
;
20 import org
.argeo
.jackrabbit
.servlet
.WebdavServlet
;
21 import org
.argeo
.jcr
.ArgeoJcrConstants
;
22 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
23 import org
.osgi
.framework
.BundleContext
;
24 import org
.osgi
.service
.http
.NamespaceException
;
25 import org
.osgi
.util
.tracker
.ServiceTracker
;
26 import org
.springframework
.security
.authentication
.AuthenticationManager
;
27 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
28 import org
.springframework
.security
.core
.Authentication
;
29 import org
.springframework
.security
.core
.context
.SecurityContext
;
30 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
33 * Intercepts and enriches http access, mainly focusing on security and
36 class NodeHttp
implements KernelConstants
, ArgeoJcrConstants
{
37 private final static Log log
= LogFactory
.getLog(NodeHttp
.class);
39 private final static String ATTR_AUTH
= "auth";
40 private final static String HEADER_AUTHORIZATION
= "Authorization";
41 private final static String HEADER_WWW_AUTHENTICATE
= "WWW-Authenticate";
43 static final String SPRING_SECURITY_CONTEXT_KEY
= "SPRING_SECURITY_CONTEXT";
45 private final AuthenticationManager authenticationManager
;
46 private final BundleContext bundleContext
;
47 private ExtendedHttpService httpService
;
49 // FIXME Make it more unique
50 private String httpAuthRealm
= "Argeo";
53 // private final RootFilter rootFilter;
56 private OpenInViewSessionProvider sessionProvider
;
57 private WebdavServlet publicWebdavServlet
;
58 private WebdavServlet privateWebdavServlet
;
59 private RemotingServlet publicRemotingServlet
;
60 private RemotingServlet privateRemotingServlet
;
62 NodeHttp(BundleContext bundleContext
, JackrabbitNode node
,
63 NodeSecurity authenticationManager
) {
64 this.bundleContext
= bundleContext
;
65 this.authenticationManager
= authenticationManager
;
68 ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
> st
= new ServiceTracker
<ExtendedHttpService
, ExtendedHttpService
>(
69 bundleContext
, ExtendedHttpService
.class, null);
72 httpService
= st
.waitForService(1000);
73 } catch (InterruptedException e
) {
77 if (httpService
== null)
78 throw new CmsException("Could not find "
79 + ExtendedHttpService
.class + " service.");
82 // rootFilter = new RootFilter();
85 sessionProvider
= new OpenInViewSessionProvider();
86 publicWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
87 privateWebdavServlet
= new WebdavServlet(node
, sessionProvider
);
88 publicRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
89 privateRemotingServlet
= new RemotingServlet(node
, sessionProvider
);
94 registerWebdavServlet(PATH_WEBDAV_PUBLIC
, ALIAS_NODE
, true,
96 registerWebdavServlet(PATH_WEBDAV_PRIVATE
, ALIAS_NODE
, false,
97 privateWebdavServlet
);
98 registerRemotingServlet(PATH_REMOTING_PUBLIC
, ALIAS_NODE
, true,
99 publicRemotingServlet
);
100 registerRemotingServlet(PATH_REMOTING_PRIVATE
, ALIAS_NODE
, false,
101 privateRemotingServlet
);
103 // httpService.registerFilter("/", rootFilter, null, null);
104 } catch (Exception e
) {
105 throw new CmsException("Cannot publish HTTP services to OSGi", e
);
109 private void registerWebdavServlet(String pathPrefix
, String alias
,
110 Boolean anonymous
, WebdavServlet webdavServlet
)
111 throws NamespaceException
, ServletException
{
112 String path
= pathPrefix
+ "/" + alias
;
113 Properties initParameters
= new Properties();
114 initParameters
.setProperty(WebdavServlet
.INIT_PARAM_RESOURCE_CONFIG
,
115 KernelConstants
.WEBDAV_CONFIG
);
116 initParameters
.setProperty(
117 WebdavServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
118 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
119 : new DavFilter(), null, null);
120 // Cast to servlet because of a weird behaviour in Eclipse
121 httpService
.registerServlet(path
, (Servlet
) webdavServlet
,
122 initParameters
, null);
125 private void registerRemotingServlet(String pathPrefix
, String alias
,
126 Boolean anonymous
, RemotingServlet remotingServlet
)
127 throws NamespaceException
, ServletException
{
128 String path
= pathPrefix
+ "/" + alias
;
129 Properties initParameters
= new Properties();
130 initParameters
.setProperty(
131 RemotingServlet
.INIT_PARAM_RESOURCE_PATH_PREFIX
, path
);
133 // Looks like a bug in Jackrabbit remoting init
134 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_HOME
,
135 KernelUtils
.getOsgiInstanceDir(bundleContext
)
136 + "/tmp/jackrabbit");
137 initParameters
.setProperty(RemotingServlet
.INIT_PARAM_TMP_DIRECTORY
,
139 // Cast to servlet because of a weird behaviour in Eclipse
140 httpService
.registerFilter(path
, anonymous ?
new AnonymousFilter()
141 : new DavFilter(), null, null);
142 httpService
.registerServlet(path
, (Servlet
) remotingServlet
,
143 initParameters
, null);
146 private Boolean
isSessionAuthenticated(HttpSession httpSession
) {
147 SecurityContext contextFromSession
= (SecurityContext
) httpSession
148 .getAttribute(SPRING_SECURITY_CONTEXT_KEY
);
149 return contextFromSession
!= null;
152 private void requestBasicAuth(HttpSession httpSession
,
153 HttpServletResponse response
) {
154 response
.setStatus(401);
155 response
.setHeader(HEADER_WWW_AUTHENTICATE
, "basic realm=\""
156 + httpAuthRealm
+ "\"");
157 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.TRUE
);
160 private UsernamePasswordAuthenticationToken
basicAuth(String authHeader
) {
161 if (authHeader
!= null) {
162 StringTokenizer st
= new StringTokenizer(authHeader
);
163 if (st
.hasMoreTokens()) {
164 String basic
= st
.nextToken();
165 if (basic
.equalsIgnoreCase("Basic")) {
167 String credentials
= new String(Base64
.decodeBase64(st
168 .nextToken()), "UTF-8");
169 log
.debug("Credentials: " + credentials
);
170 int p
= credentials
.indexOf(":");
172 String login
= credentials
.substring(0, p
).trim();
173 String password
= credentials
.substring(p
+ 1)
176 return new UsernamePasswordAuthenticationToken(
179 throw new CmsException(
180 "Invalid authentication token");
182 } catch (Exception e
) {
183 throw new CmsException(
184 "Couldn't retrieve authentication", e
);
189 throw new CmsException("Couldn't retrieve authentication");
192 /** Intercepts all requests. Authenticates. */
193 class RootFilter
extends HttpFilter
{
196 public void doFilter(HttpSession httpSession
,
197 HttpServletRequest request
, HttpServletResponse response
,
198 FilterChain filterChain
) throws IOException
, ServletException
{
200 // Authenticate from session
201 if (isSessionAuthenticated(httpSession
)) {
202 filterChain
.doFilter(request
, response
);
210 // Process basic auth
211 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
212 if (basicAuth
!= null) {
213 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
214 Authentication auth
= authenticationManager
.authenticate(token
);
215 SecurityContextHolder
.getContext().setAuthentication(auth
);
216 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
,
217 SecurityContextHolder
.getContext());
218 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.FALSE
);
219 filterChain
.doFilter(request
, response
);
223 Boolean doBasicAuth
= true;
225 requestBasicAuth(httpSession
, response
);
233 KernelUtils
.anonymousLogin(authenticationManager
);
234 filterChain
.doFilter(request
, response
);
238 /** Intercepts all requests. Authenticates. */
239 class AnonymousFilter
extends HttpFilter
{
241 public void doFilter(HttpSession httpSession
,
242 HttpServletRequest request
, HttpServletResponse response
,
243 FilterChain filterChain
) throws IOException
, ServletException
{
245 // Authenticate from session
246 if (isSessionAuthenticated(httpSession
)) {
247 filterChain
.doFilter(request
, response
);
251 KernelUtils
.anonymousLogin(authenticationManager
);
252 filterChain
.doFilter(request
, response
);
256 /** Intercepts all requests. Authenticates. */
257 class DavFilter
extends HttpFilter
{
260 public void doFilter(HttpSession httpSession
,
261 HttpServletRequest request
, HttpServletResponse response
,
262 FilterChain filterChain
) throws IOException
, ServletException
{
264 // Authenticate from session
265 if (isSessionAuthenticated(httpSession
)) {
266 filterChain
.doFilter(request
, response
);
270 // Process basic auth
271 String basicAuth
= request
.getHeader(HEADER_AUTHORIZATION
);
272 if (basicAuth
!= null) {
273 UsernamePasswordAuthenticationToken token
= basicAuth(basicAuth
);
274 Authentication auth
= authenticationManager
.authenticate(token
);
275 SecurityContextHolder
.getContext().setAuthentication(auth
);
276 httpSession
.setAttribute(SPRING_SECURITY_CONTEXT_KEY
,
277 SecurityContextHolder
.getContext());
278 httpSession
.setAttribute(ATTR_AUTH
, Boolean
.FALSE
);
279 filterChain
.doFilter(request
, response
);
283 requestBasicAuth(httpSession
, response
);