]> git.argeo.org Git - lgpl/argeo-commons.git/blob - cms/internal/kernel/NodeHttp.java
Prepare next development cycle
[lgpl/argeo-commons.git] / cms / internal / kernel / NodeHttp.java
1 package org.argeo.cms.internal.kernel;
2
3 import static org.argeo.jackrabbit.servlet.WebdavServlet.INIT_PARAM_RESOURCE_CONFIG;
4
5 import java.io.IOException;
6 import java.util.Enumeration;
7 import java.util.Properties;
8 import java.util.StringTokenizer;
9
10 import javax.jcr.Repository;
11 import javax.servlet.FilterChain;
12 import javax.servlet.Servlet;
13 import javax.servlet.ServletException;
14 import javax.servlet.http.HttpServletRequest;
15 import javax.servlet.http.HttpServletResponse;
16 import javax.servlet.http.HttpSession;
17
18 import org.apache.commons.codec.binary.Base64;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.argeo.cms.CmsException;
22 import org.argeo.jackrabbit.servlet.OpenInViewSessionProvider;
23 import org.argeo.jackrabbit.servlet.RemotingServlet;
24 import org.argeo.jackrabbit.servlet.WebdavServlet;
25 import org.argeo.jcr.ArgeoJcrConstants;
26 import org.argeo.security.NodeAuthenticationToken;
27 import org.eclipse.equinox.http.servlet.ExtendedHttpService;
28 import org.osgi.service.http.NamespaceException;
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;
34
35 /**
36 * Intercepts and enriches http access, mainly focusing on security and
37 * transactionality.
38 */
39 class NodeHttp implements KernelConstants, ArgeoJcrConstants {
40 private final static Log log = LogFactory.getLog(NodeHttp.class);
41
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";
45
46 private final AuthenticationManager authenticationManager;
47 private final ExtendedHttpService httpService;
48
49 // FIXME Make it more unique
50 private String httpAuthRealm = "Argeo";
51
52 // Filters
53 private final RootFilter rootFilter;
54 // private final DoSFilter dosFilter;
55 // private final QoSFilter qosFilter;
56
57 // WebDav / JCR remoting
58 private OpenInViewSessionProvider sessionProvider;
59
60 NodeHttp(ExtendedHttpService httpService, JackrabbitNode node,
61 NodeSecurity authenticationManager) {
62 // this.bundleContext = bundleContext;
63 this.authenticationManager = authenticationManager;
64
65 this.httpService = httpService;
66
67 // Filters
68 rootFilter = new RootFilter();
69 // dosFilter = new CustomDosFilter();
70 // qosFilter = new QoSFilter();
71
72 // DAV
73 sessionProvider = new OpenInViewSessionProvider();
74
75 try {
76 registerWebdavServlet(ALIAS_NODE, node, true);
77 registerWebdavServlet(ALIAS_NODE, node, false);
78 registerRemotingServlet(ALIAS_NODE, node, true);
79 registerRemotingServlet(ALIAS_NODE, node, false);
80
81 httpService.registerFilter("/", rootFilter, null, null);
82 } catch (Exception e) {
83 throw new CmsException("Could not initialise http", e);
84 }
85 }
86
87 public void destroy() {
88 sessionProvider.destroy();
89 }
90
91 void registerWebdavServlet(String alias, Repository repository,
92 boolean anonymous) throws NamespaceException, ServletException {
93 WebdavServlet webdavServlet = new WebdavServlet(repository,
94 sessionProvider);
95 String pathPrefix = anonymous ? WEBDAV_PUBLIC : WEBDAV_PRIVATE;
96 String path = pathPrefix + "/" + alias;
97 Properties ip = new Properties();
98 ip.setProperty(INIT_PARAM_RESOURCE_CONFIG, WEBDAV_CONFIG);
99 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
100 httpService.registerFilter(path, anonymous ? new AnonymousFilter()
101 : new DavFilter(), null, null);
102 // Cast to servlet because of a weird behaviour in Eclipse
103 httpService.registerServlet(path, (Servlet) webdavServlet, ip, null);
104 }
105
106 void registerRemotingServlet(String alias, Repository repository,
107 boolean anonymous) throws NamespaceException, ServletException {
108 String pathPrefix = anonymous ? REMOTING_PUBLIC : REMOTING_PRIVATE;
109 RemotingServlet remotingServlet = new RemotingServlet(repository,
110 sessionProvider);
111 String path = pathPrefix + "/" + alias;
112 Properties ip = new Properties();
113 ip.setProperty(RemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
114
115 // Looks like a bug in Jackrabbit remoting init
116 ip.setProperty(RemotingServlet.INIT_PARAM_HOME,
117 KernelUtils.getOsgiInstanceDir() + "/tmp/jackrabbit");
118 ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting");
119 // Cast to servlet because of a weird behaviour in Eclipse
120 httpService.registerFilter(path, anonymous ? new AnonymousFilter()
121 : new DavFilter(), null, null);
122 httpService.registerServlet(path, (Servlet) remotingServlet, ip, null);
123 }
124
125 private Boolean isSessionAuthenticated(HttpSession httpSession) {
126 SecurityContext contextFromSession = (SecurityContext) httpSession
127 .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
128 return contextFromSession != null;
129 }
130
131 private void requestBasicAuth(HttpSession httpSession,
132 HttpServletResponse response) {
133 response.setStatus(401);
134 response.setHeader(HEADER_WWW_AUTHENTICATE, "basic realm=\""
135 + httpAuthRealm + "\"");
136 httpSession.setAttribute(ATTR_AUTH, Boolean.TRUE);
137 }
138
139 private NodeAuthenticationToken basicAuth(String authHeader) {
140 if (authHeader != null) {
141 StringTokenizer st = new StringTokenizer(authHeader);
142 if (st.hasMoreTokens()) {
143 String basic = st.nextToken();
144 if (basic.equalsIgnoreCase("Basic")) {
145 try {
146 String credentials = new String(Base64.decodeBase64(st
147 .nextToken()), "UTF-8");
148 // log.debug("Credentials: " + credentials);
149 int p = credentials.indexOf(":");
150 if (p != -1) {
151 String login = credentials.substring(0, p).trim();
152 String password = credentials.substring(p + 1)
153 .trim();
154
155 return new NodeAuthenticationToken(login,
156 password.toCharArray());
157 } else {
158 throw new CmsException(
159 "Invalid authentication token");
160 }
161 } catch (Exception e) {
162 throw new CmsException(
163 "Couldn't retrieve authentication", e);
164 }
165 }
166 }
167 }
168 throw new CmsException("Couldn't retrieve authentication");
169 }
170
171 /** Intercepts all requests. Authenticates. */
172 class RootFilter extends HttpFilter {
173
174 @Override
175 public void doFilter(HttpSession httpSession,
176 HttpServletRequest request, HttpServletResponse response,
177 FilterChain filterChain) throws IOException, ServletException {
178 if (log.isTraceEnabled()) {
179 log.debug(request.getContextPath());
180 log.debug(request.getServletPath());
181 log.debug(request.getRequestURI());
182 log.debug(request.getQueryString());
183 StringBuilder buf = new StringBuilder();
184 Enumeration<String> en = request.getHeaderNames();
185 while (en.hasMoreElements()) {
186 String header = en.nextElement();
187 Enumeration<String> values = request.getHeaders(header);
188 while (values.hasMoreElements())
189 buf.append(" " + header + ": " + values.nextElement());
190 buf.append('\n');
191 }
192 log.debug("\n" + buf);
193 }
194
195 String servletPath = request.getServletPath();
196
197 // skip data
198 if (servletPath.startsWith(PATH_DATA)) {
199 filterChain.doFilter(request, response);
200 return;
201 }
202
203 // skip /ui (workbench) for the time being
204 if (servletPath.startsWith(PATH_WORKBENCH)) {
205 filterChain.doFilter(request, response);
206 return;
207 }
208
209 // redirect long RWT paths to anchor
210 String path = request.getRequestURI().substring(
211 servletPath.length());
212 int pathLength = path.length();
213 if (pathLength != 0 && (path.charAt(0) == '/')
214 && !servletPath.endsWith("rwt-resources")
215 && !path.equals("/")) {
216 String newLocation = request.getServletPath() + "#" + path;
217 response.setHeader("Location", newLocation);
218 response.setStatus(HttpServletResponse.SC_FOUND);
219 return;
220 }
221
222 // process normally
223 filterChain.doFilter(request, response);
224 }
225 }
226
227 /** Intercepts all requests. Authenticates. */
228 private class AnonymousFilter extends HttpFilter {
229 @Override
230 public void doFilter(HttpSession httpSession,
231 HttpServletRequest request, HttpServletResponse response,
232 FilterChain filterChain) throws IOException, ServletException {
233
234 // Authenticate from session
235 if (isSessionAuthenticated(httpSession)) {
236 filterChain.doFilter(request, response);
237 return;
238 }
239
240 KernelUtils.anonymousLogin(authenticationManager);
241 filterChain.doFilter(request, response);
242 }
243 }
244
245 /** Intercepts all requests. Authenticates. */
246 private class DavFilter extends HttpFilter {
247
248 @Override
249 public void doFilter(HttpSession httpSession,
250 HttpServletRequest request, HttpServletResponse response,
251 FilterChain filterChain) throws IOException, ServletException {
252
253 // Authenticate from session
254 // if (isSessionAuthenticated(httpSession)) {
255 // filterChain.doFilter(request, response);
256 // return;
257 // }
258
259 // Process basic auth
260 String basicAuth = request.getHeader(HEADER_AUTHORIZATION);
261 if (basicAuth != null) {
262 UsernamePasswordAuthenticationToken token = basicAuth(basicAuth);
263 Authentication auth = authenticationManager.authenticate(token);
264 SecurityContextHolder.getContext().setAuthentication(auth);
265 httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,
266 SecurityContextHolder.getContext());
267 httpSession.setAttribute(ATTR_AUTH, Boolean.FALSE);
268 filterChain.doFilter(request, response);
269 return;
270 }
271
272 requestBasicAuth(httpSession, response);
273 }
274 }
275
276 // class CustomDosFilter extends DoSFilter {
277 // @Override
278 // protected String extractUserId(ServletRequest request) {
279 // HttpSession httpSession = ((HttpServletRequest) request)
280 // .getSession();
281 // if (isSessionAuthenticated(httpSession)) {
282 // String userId = ((SecurityContext) httpSession
283 // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
284 // .getAuthentication().getName();
285 // return userId;
286 // }
287 // return super.extractUserId(request);
288 //
289 // }
290 // }
291 }