]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
Use char array for password
[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 java.io.IOException;
4 import java.util.Properties;
5 import java.util.StringTokenizer;
6
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;
13
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;
33
34 /**
35 * Intercepts and enriches http access, mainly focusing on security and
36 * transactionality.
37 */
38 class NodeHttp implements KernelConstants, ArgeoJcrConstants {
39 private final static Log log = LogFactory.getLog(NodeHttp.class);
40
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";
44
45 static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
46
47 private final AuthenticationManager authenticationManager;
48 private final BundleContext bundleContext;
49 private ExtendedHttpService httpService;
50
51 // FIXME Make it more unique
52 private String httpAuthRealm = "Argeo";
53
54 // Filters
55 private final RootFilter rootFilter;
56
57 // remoting
58 private OpenInViewSessionProvider sessionProvider;
59 private WebdavServlet publicWebdavServlet;
60 private WebdavServlet privateWebdavServlet;
61 private RemotingServlet publicRemotingServlet;
62 private RemotingServlet privateRemotingServlet;
63
64 NodeHttp(BundleContext bundleContext, JackrabbitNode node,
65 NodeSecurity authenticationManager) {
66 this.bundleContext = bundleContext;
67 this.authenticationManager = authenticationManager;
68
69 // Equinox dependency
70 ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
71 bundleContext, ExtendedHttpService.class, null);
72 st.open();
73 try {
74 httpService = st.waitForService(1000);
75 } catch (InterruptedException e) {
76 httpService = null;
77 }
78
79 if (httpService == null)
80 throw new CmsException("Could not find "
81 + ExtendedHttpService.class + " service.");
82
83 // Filters
84 rootFilter = new RootFilter();
85
86 // DAV
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);
92 }
93
94 void publish() {
95 try {
96 registerWebdavServlet(PATH_WEBDAV_PUBLIC, ALIAS_NODE, true,
97 publicWebdavServlet);
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);
104
105 // httpService.registerFilter("/", rootFilter, null, null);
106 } catch (Exception e) {
107 throw new CmsException("Cannot publish HTTP services to OSGi", e);
108 }
109 }
110
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);
125 }
126
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);
134
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,
140 "remoting");
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);
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 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")) {
168 try {
169 String credentials = new String(Base64.decodeBase64(st
170 .nextToken()), "UTF-8");
171 log.debug("Credentials: " + credentials);
172 int p = credentials.indexOf(":");
173 if (p != -1) {
174 String login = credentials.substring(0, p).trim();
175 String password = credentials.substring(p + 1)
176 .trim();
177
178 return new UsernamePasswordAuthenticationToken(
179 login, password);
180 } else {
181 throw new CmsException(
182 "Invalid authentication token");
183 }
184 } catch (Exception e) {
185 throw new CmsException(
186 "Couldn't retrieve authentication", e);
187 }
188 }
189 }
190 }
191 throw new CmsException("Couldn't retrieve authentication");
192 }
193
194 /** Intercepts all requests. Authenticates. */
195 class RootFilter extends HttpFilter {
196
197 @Override
198 public void doFilter(HttpSession httpSession,
199 HttpServletRequest request, HttpServletResponse response,
200 FilterChain filterChain) throws IOException, ServletException {
201
202 // Authenticate from session
203 if (isSessionAuthenticated(httpSession)) {
204 filterChain.doFilter(request, response);
205 return;
206 }
207
208 // TODO Kerberos
209
210 // TODO Certificate
211
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);
222 return;
223 }
224
225 Boolean doBasicAuth = true;
226 if (doBasicAuth) {
227 requestBasicAuth(httpSession, response);
228 // skip filter chain
229 return;
230 }
231
232 // TODO Login page
233
234 // Anonymous
235 KernelUtils.anonymousLogin(authenticationManager);
236 filterChain.doFilter(request, response);
237 }
238 }
239
240 /** Intercepts all requests. Authenticates. */
241 class AnonymousFilter extends HttpFilter {
242 @Override
243 public void doFilter(HttpSession httpSession,
244 HttpServletRequest request, HttpServletResponse response,
245 FilterChain filterChain) throws IOException, ServletException {
246
247 // Authenticate from session
248 if (isSessionAuthenticated(httpSession)) {
249 filterChain.doFilter(request, response);
250 return;
251 }
252
253 KernelUtils.anonymousLogin(authenticationManager);
254 filterChain.doFilter(request, response);
255 }
256 }
257
258 /** Intercepts all requests. Authenticates. */
259 class DavFilter extends HttpFilter {
260
261 @Override
262 public void doFilter(HttpSession httpSession,
263 HttpServletRequest request, HttpServletResponse response,
264 FilterChain filterChain) throws IOException, ServletException {
265
266 // Authenticate from session
267 if (isSessionAuthenticated(httpSession)) {
268 filterChain.doFilter(request, response);
269 return;
270 }
271
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);
282 return;
283 }
284
285 requestBasicAuth(httpSession, response);
286 }
287 }
288
289 }