]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
Remove unused directory
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / NodeHttp.java
1 package org.argeo.cms.internal.kernel;
2
3 import static org.argeo.cms.auth.AuthConstants.ACCESS_CONTROL_CONTEXT;
4
5 import java.io.IOException;
6 import java.security.AccessControlContext;
7 import java.security.AccessController;
8 import java.security.PrivilegedActionException;
9 import java.security.PrivilegedExceptionAction;
10 import java.security.cert.X509Certificate;
11 import java.util.Enumeration;
12 import java.util.Properties;
13 import java.util.StringTokenizer;
14
15 import javax.jcr.Repository;
16 import javax.security.auth.Subject;
17 import javax.security.auth.callback.Callback;
18 import javax.security.auth.callback.CallbackHandler;
19 import javax.security.auth.callback.NameCallback;
20 import javax.security.auth.callback.PasswordCallback;
21 import javax.security.auth.login.LoginContext;
22 import javax.security.auth.login.LoginException;
23 import javax.servlet.FilterChain;
24 import javax.servlet.Servlet;
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28 import javax.servlet.http.HttpSession;
29
30 import org.apache.commons.codec.binary.Base64;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.argeo.cms.CmsException;
34 import org.argeo.cms.auth.AuthConstants;
35 import org.argeo.jackrabbit.servlet.OpenInViewSessionProvider;
36 import org.argeo.jackrabbit.servlet.RemotingServlet;
37 import org.argeo.jackrabbit.servlet.WebdavServlet;
38 import org.argeo.jcr.ArgeoJcrConstants;
39 import org.eclipse.equinox.http.servlet.ExtendedHttpService;
40 import org.osgi.service.http.NamespaceException;
41
42 /**
43 * Intercepts and enriches http access, mainly focusing on security and
44 * transactionality.
45 */
46 @Deprecated
47 class NodeHttp implements KernelConstants, ArgeoJcrConstants {
48 private final static Log log = LogFactory.getLog(NodeHttp.class);
49
50 private final static String ATTR_AUTH = "auth";
51 private final static String HEADER_AUTHORIZATION = "Authorization";
52 private final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
53
54 // private final AuthenticationManager authenticationManager;
55 private final ExtendedHttpService httpService;
56
57 // FIXME Make it more unique
58 private String httpAuthRealm = "Argeo";
59
60 // Filters
61 private final RootFilter rootFilter;
62 // private final DoSFilter dosFilter;
63 // private final QoSFilter qosFilter;
64
65 // WebDav / JCR remoting
66 private OpenInViewSessionProvider sessionProvider;
67
68 NodeHttp(ExtendedHttpService httpService, NodeRepository node) {
69 // this.bundleContext = bundleContext;
70 // this.authenticationManager = authenticationManager;
71
72 this.httpService = httpService;
73
74 // Filters
75 rootFilter = new RootFilter();
76 // dosFilter = new CustomDosFilter();
77 // qosFilter = new QoSFilter();
78
79 // DAV
80 sessionProvider = new OpenInViewSessionProvider();
81
82 registerRepositoryServlets(ALIAS_NODE, node);
83 try {
84 httpService.registerFilter("/", rootFilter, null, null);
85 } catch (Exception e) {
86 throw new CmsException("Could not register root filter", e);
87 }
88 }
89
90 public void destroy() {
91 sessionProvider.destroy();
92 unregisterRepositoryServlets(ALIAS_NODE);
93 }
94
95 void registerRepositoryServlets(String alias, Repository repository) {
96 try {
97 registerWebdavServlet(alias, repository, true);
98 registerWebdavServlet(alias, repository, false);
99 registerRemotingServlet(alias, repository, true);
100 registerRemotingServlet(alias, repository, false);
101 } catch (Exception e) {
102 throw new CmsException(
103 "Could not register servlets for repository " + alias, e);
104 }
105 }
106
107 void unregisterRepositoryServlets(String alias) {
108 // FIXME unregister servlets
109 }
110
111 void registerWebdavServlet(String alias, Repository repository,
112 boolean anonymous) throws NamespaceException, ServletException {
113 WebdavServlet webdavServlet = new WebdavServlet(repository,
114 sessionProvider);
115 String pathPrefix = anonymous ? WEBDAV_PUBLIC : WEBDAV_PRIVATE;
116 String path = pathPrefix + "/" + alias;
117 Properties ip = new Properties();
118 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, WEBDAV_CONFIG);
119 ip.setProperty(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, ip, null);
124 }
125
126 void registerRemotingServlet(String alias, Repository repository,
127 boolean anonymous) throws NamespaceException, ServletException {
128 String pathPrefix = anonymous ? REMOTING_PUBLIC : REMOTING_PRIVATE;
129 RemotingServlet remotingServlet = new RemotingServlet(repository,
130 sessionProvider);
131 String path = pathPrefix + "/" + alias;
132 Properties ip = new Properties();
133 ip.setProperty(RemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
134
135 // Looks like a bug in Jackrabbit remoting init
136 ip.setProperty(RemotingServlet.INIT_PARAM_HOME,
137 KernelUtils.getOsgiInstanceDir() + "/tmp/jackrabbit");
138 ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting");
139 // in order to avoid annoying warning.
140 ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG,
141 "");
142 // Cast to servlet because of a weird behaviour in Eclipse
143 httpService.registerFilter(path, anonymous ? new AnonymousFilter()
144 : new DavFilter(), null, null);
145 httpService.registerServlet(path, (Servlet) remotingServlet, ip, null);
146 }
147
148 // private Boolean isSessionAuthenticated(HttpSession httpSession) {
149 // SecurityContext contextFromSession = (SecurityContext) httpSession
150 // .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
151 // return contextFromSession != null;
152 // }
153
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);
160 }
161
162 private CallbackHandler 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")) {
168 try {
169 // TODO manipulate char[]
170 String credentials = new String(Base64.decodeBase64(st
171 .nextToken()), "UTF-8");
172 // log.debug("Credentials: " + credentials);
173 int p = credentials.indexOf(":");
174 if (p != -1) {
175 final String login = credentials.substring(0, p)
176 .trim();
177 final char[] password = credentials
178 .substring(p + 1).trim().toCharArray();
179
180 return new CallbackHandler() {
181 public void handle(Callback[] callbacks) {
182 for (Callback cb : callbacks) {
183 if (cb instanceof NameCallback)
184 ((NameCallback) cb).setName(login);
185 else if (cb instanceof PasswordCallback)
186 ((PasswordCallback) cb)
187 .setPassword(password);
188 }
189 }
190 };
191 } else {
192 throw new CmsException(
193 "Invalid authentication token");
194 }
195 } catch (Exception e) {
196 throw new CmsException(
197 "Couldn't retrieve authentication", e);
198 }
199 }
200 }
201 }
202 throw new CmsException("Couldn't retrieve authentication");
203 }
204
205 /** Intercepts all requests. Authenticates. */
206 class RootFilter extends HttpFilter {
207
208 @Override
209 public void doFilter(HttpSession httpSession,
210 HttpServletRequest request, HttpServletResponse response,
211 FilterChain filterChain) throws IOException, ServletException {
212 if (log.isTraceEnabled()) {
213 log.trace(request.getRequestURL().append(
214 request.getQueryString() != null ? "?"
215 + request.getQueryString() : ""));
216 logRequest(request);
217 }
218
219 String servletPath = request.getServletPath();
220
221 // client certificate
222 X509Certificate clientCert = extractCertificate(request);
223 if (clientCert != null) {
224 // TODO authenticate
225 // if (log.isDebugEnabled())
226 // log.debug(clientCert.getSubjectX500Principal().getName());
227 }
228
229 // skip data
230 if (servletPath.startsWith(PATH_DATA)) {
231 filterChain.doFilter(request, response);
232 return;
233 }
234
235 // skip /ui (workbench) for the time being
236 if (servletPath.startsWith(PATH_WORKBENCH)) {
237 filterChain.doFilter(request, response);
238 return;
239 }
240
241 // redirect long RWT paths to anchor
242 String path = request.getRequestURI().substring(
243 servletPath.length());
244 int pathLength = path.length();
245 if (pathLength != 0 && (path.charAt(0) == '/')
246 && !servletPath.endsWith("rwt-resources")
247 && !path.startsWith(KernelConstants.PATH_WORKBENCH)
248 && path.lastIndexOf('/') != 0) {
249 String newLocation = request.getServletPath() + "#" + path;
250 response.setHeader("Location", newLocation);
251 response.setStatus(HttpServletResponse.SC_FOUND);
252 return;
253 }
254
255 // process normally
256 filterChain.doFilter(request, response);
257 }
258 }
259
260 private void logRequest(HttpServletRequest request) {
261 log.debug("contextPath=" + request.getContextPath());
262 log.debug("servletPath=" + request.getServletPath());
263 log.debug("requestURI=" + request.getRequestURI());
264 log.debug("queryString=" + request.getQueryString());
265 StringBuilder buf = new StringBuilder();
266 // headers
267 Enumeration<String> en = request.getHeaderNames();
268 while (en.hasMoreElements()) {
269 String header = en.nextElement();
270 Enumeration<String> values = request.getHeaders(header);
271 while (values.hasMoreElements())
272 buf.append(" " + header + ": " + values.nextElement());
273 buf.append('\n');
274 }
275
276 // attributed
277 Enumeration<String> an = request.getAttributeNames();
278 while (an.hasMoreElements()) {
279 String attr = an.nextElement();
280 Object value = request.getAttribute(attr);
281 buf.append(" " + attr + ": " + value);
282 buf.append('\n');
283 }
284 log.debug("\n" + buf);
285 }
286
287 private X509Certificate extractCertificate(HttpServletRequest req) {
288 X509Certificate[] certs = (X509Certificate[]) req
289 .getAttribute("javax.servlet.request.X509Certificate");
290 if (null != certs && certs.length > 0) {
291 return certs[0];
292 }
293 return null;
294 }
295
296 /** Intercepts all requests. Authenticates. */
297 private class AnonymousFilter extends HttpFilter {
298 @Override
299 public void doFilter(HttpSession httpSession,
300 final HttpServletRequest request,
301 final HttpServletResponse response,
302 final FilterChain filterChain) throws IOException,
303 ServletException {
304
305 // Authenticate from session
306 // if (isSessionAuthenticated(httpSession)) {
307 // filterChain.doFilter(request, response);
308 // return;
309 // }
310
311 Subject subject = KernelUtils.anonymousLogin();
312 try {
313 Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
314 public Void run() throws IOException, ServletException {
315 filterChain.doFilter(request, response);
316 return null;
317 }
318 });
319 } catch (PrivilegedActionException e) {
320 if (e.getCause() instanceof ServletException)
321 throw (ServletException) e.getCause();
322 else if (e.getCause() instanceof IOException)
323 throw (IOException) e.getCause();
324 else
325 throw new CmsException("Unexpected exception", e.getCause());
326 }
327 }
328 }
329
330 /** Intercepts all requests. Authenticates. */
331 private class DavFilter extends HttpFilter {
332
333 @Override
334 public void doFilter(final HttpSession httpSession,
335 final HttpServletRequest request,
336 final HttpServletResponse response,
337 final FilterChain filterChain) throws IOException,
338 ServletException {
339
340 AccessControlContext acc = (AccessControlContext) httpSession
341 .getAttribute(AuthConstants.ACCESS_CONTROL_CONTEXT);
342 final Subject subject;
343 if (acc != null) {
344 subject = Subject.getSubject(acc);
345 } else {
346 // Process basic auth
347 String basicAuth = request.getHeader(HEADER_AUTHORIZATION);
348 if (basicAuth != null) {
349 CallbackHandler token = basicAuth(basicAuth);
350 try {
351 LoginContext lc = new LoginContext(
352 AuthConstants.LOGIN_CONTEXT_USER, token);
353 lc.login();
354 subject = lc.getSubject();
355 } catch (LoginException e) {
356 throw new CmsException("Could not login", e);
357 }
358 } else {
359 requestBasicAuth(httpSession, response);
360 return;
361 }
362 }
363 // do filter as subject
364 try {
365 Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
366 public Void run() throws IOException, ServletException {
367 // add security context to session
368 httpSession.setAttribute(ACCESS_CONTROL_CONTEXT,
369 AccessController.getContext());
370 filterChain.doFilter(request, response);
371 return null;
372 }
373 });
374 } catch (PrivilegedActionException e) {
375 if (e.getCause() instanceof ServletException)
376 throw (ServletException) e.getCause();
377 else if (e.getCause() instanceof IOException)
378 throw (IOException) e.getCause();
379 else
380 throw new CmsException("Unexpected exception", e.getCause());
381 }
382
383 }
384 }
385
386 // class CustomDosFilter extends DoSFilter {
387 // @Override
388 // protected String extractUserId(ServletRequest request) {
389 // HttpSession httpSession = ((HttpServletRequest) request)
390 // .getSession();
391 // if (isSessionAuthenticated(httpSession)) {
392 // String userId = ((SecurityContext) httpSession
393 // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
394 // .getAuthentication().getName();
395 // return userId;
396 // }
397 // return super.extractUserId(request);
398 //
399 // }
400 // }
401 }