]> git.argeo.org Git - lgpl/argeo-commons.git/blob - auth/HttpSessionLoginModule.java
Prepare next development cycle
[lgpl/argeo-commons.git] / auth / HttpSessionLoginModule.java
1 package org.argeo.cms.auth;
2
3 import java.io.IOException;
4 import java.security.cert.X509Certificate;
5 import java.util.Base64;
6 import java.util.Locale;
7 import java.util.Map;
8 import java.util.StringTokenizer;
9
10 import javax.security.auth.Subject;
11 import javax.security.auth.callback.Callback;
12 import javax.security.auth.callback.CallbackHandler;
13 import javax.security.auth.callback.UnsupportedCallbackException;
14 import javax.security.auth.login.LoginException;
15 import javax.security.auth.spi.LoginModule;
16 import javax.servlet.http.HttpServletRequest;
17 import javax.servlet.http.HttpServletResponse;
18 import javax.servlet.http.HttpSession;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.argeo.cms.CmsException;
23 import org.argeo.cms.internal.kernel.Activator;
24 import org.osgi.framework.BundleContext;
25 import org.osgi.framework.FrameworkUtil;
26 import org.osgi.service.http.HttpContext;
27 import org.osgi.service.useradmin.Authorization;
28
29 /** Use the HTTP session as the basis for authentication. */
30 public class HttpSessionLoginModule implements LoginModule {
31 private final static Log log = LogFactory.getLog(HttpSessionLoginModule.class);
32
33 private Subject subject = null;
34 private CallbackHandler callbackHandler = null;
35 private Map<String, Object> sharedState = null;
36
37 private HttpServletRequest request = null;
38 private HttpServletResponse response = null;
39
40 private BundleContext bc;
41
42 private Authorization authorization;
43 private Locale locale;
44
45 @SuppressWarnings("unchecked")
46 @Override
47 public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
48 Map<String, ?> options) {
49 bc = FrameworkUtil.getBundle(HttpSessionLoginModule.class).getBundleContext();
50 assert bc != null;
51 this.subject = subject;
52 this.callbackHandler = callbackHandler;
53 this.sharedState = (Map<String, Object>) sharedState;
54 }
55
56 @Override
57 public boolean login() throws LoginException {
58 if (callbackHandler == null)
59 return false;
60 HttpRequestCallback httpCallback = new HttpRequestCallback();
61 try {
62 callbackHandler.handle(new Callback[] { httpCallback });
63 } catch (IOException e) {
64 throw new LoginException("Cannot handle http callback: " + e.getMessage());
65 } catch (UnsupportedCallbackException e) {
66 return false;
67 }
68 request = httpCallback.getRequest();
69 if (request == null) {
70 HttpSession httpSession = httpCallback.getHttpSession();
71 if (httpSession == null)
72 return false;
73 // TODO factorize with below
74 String httpSessionId = httpSession.getId();
75 if (log.isTraceEnabled())
76 log.trace("HTTP login: " + request.getPathInfo() + " #" + httpSessionId);
77 CmsSession cmsSession = CmsAuthUtils.cmsSessionFromHttpSession(bc, httpSessionId);
78 if (cmsSession != null) {
79 authorization = cmsSession.getAuthorization();
80 locale = cmsSession.getLocale();
81 if (log.isTraceEnabled())
82 log.trace("Retrieved authorization from " + cmsSession);
83 }
84 } else {
85 authorization = (Authorization) request.getAttribute(HttpContext.AUTHORIZATION);
86 if (authorization == null) {// search by session ID
87 HttpSession httpSession = request.getSession(false);
88 if (httpSession == null) {
89 // TODO make sure this is always safe
90 if (log.isTraceEnabled())
91 log.trace("Create http session");
92 httpSession = request.getSession(true);
93 }
94 String httpSessionId = httpSession.getId();
95 if (log.isTraceEnabled())
96 log.trace("HTTP login: " + request.getPathInfo() + " #" + httpSessionId);
97 CmsSession cmsSession = CmsAuthUtils.cmsSessionFromHttpSession(bc, httpSessionId);
98 if (cmsSession != null) {
99 authorization = cmsSession.getAuthorization();
100 locale = cmsSession.getLocale();
101 if (log.isTraceEnabled())
102 log.trace("Retrieved authorization from " + cmsSession);
103 }
104 }
105 sharedState.put(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST, request);
106 extractHttpAuth(request);
107 extractClientCertificate(request);
108 }
109 if (authorization == null) {
110 if (log.isTraceEnabled())
111 log.trace("HTTP login: " + false);
112 return false;
113 } else {
114 if (log.isTraceEnabled())
115 log.trace("HTTP login: " + true);
116 request.setAttribute(HttpContext.AUTHORIZATION, authorization);
117 return true;
118 }
119 }
120
121 @Override
122 public boolean commit() throws LoginException {
123 byte[] outToken = (byte[]) sharedState.get(CmsAuthUtils.SHARED_STATE_SPNEGO_OUT_TOKEN);
124 if (outToken != null) {
125 response.setHeader(CmsAuthUtils.HEADER_WWW_AUTHENTICATE,
126 "Negotiate " + java.util.Base64.getEncoder().encodeToString(outToken));
127 }
128
129 if (authorization != null) {
130 // Locale locale = request.getLocale();
131 if (locale == null && request != null)
132 locale = request.getLocale();
133 if (locale != null)
134 subject.getPublicCredentials().add(locale);
135 CmsAuthUtils.addAuthorization(subject, authorization);
136 CmsAuthUtils.registerSessionAuthorization(request, subject, authorization, locale);
137 cleanUp();
138 return true;
139 } else {
140 cleanUp();
141 return false;
142 }
143 }
144
145 @Override
146 public boolean abort() throws LoginException {
147 cleanUp();
148 return false;
149 }
150
151 private void cleanUp() {
152 authorization = null;
153 request = null;
154 }
155
156 @Override
157 public boolean logout() throws LoginException {
158 cleanUp();
159 return true;
160 }
161
162 private void extractHttpAuth(final HttpServletRequest httpRequest) {
163 String authHeader = httpRequest.getHeader(CmsAuthUtils.HEADER_AUTHORIZATION);
164 extractHttpAuth(authHeader);
165 }
166
167 private void extractHttpAuth(String authHeader) {
168 if (authHeader != null) {
169 StringTokenizer st = new StringTokenizer(authHeader);
170 if (st.hasMoreTokens()) {
171 String basic = st.nextToken();
172 if (basic.equalsIgnoreCase("Basic")) {
173 try {
174 // TODO manipulate char[]
175 Base64.Decoder decoder = Base64.getDecoder();
176 String credentials = new String(decoder.decode(st.nextToken()), "UTF-8");
177 // log.debug("Credentials: " + credentials);
178 int p = credentials.indexOf(":");
179 if (p != -1) {
180 final String login = credentials.substring(0, p).trim();
181 final char[] password = credentials.substring(p + 1).trim().toCharArray();
182 sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, login);
183 sharedState.put(CmsAuthUtils.SHARED_STATE_PWD, password);
184 } else {
185 throw new CmsException("Invalid authentication token");
186 }
187 } catch (Exception e) {
188 throw new CmsException("Couldn't retrieve authentication", e);
189 }
190 } else if (basic.equalsIgnoreCase("Negotiate")) {
191 String spnegoToken = st.nextToken();
192 Base64.Decoder decoder = Base64.getDecoder();
193 byte[] authToken = decoder.decode(spnegoToken);
194 sharedState.put(CmsAuthUtils.SHARED_STATE_SPNEGO_TOKEN, authToken);
195 }
196 }
197 }
198
199 // auth token
200 // String mail = request.getParameter(LdapAttrs.mail.name());
201 // String authPassword = request.getParameter(LdapAttrs.authPassword.name());
202 // if (authPassword != null) {
203 // sharedState.put(CmsAuthUtils.SHARED_STATE_PWD, authPassword);
204 // if (mail != null)
205 // sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, mail);
206 // }
207 }
208
209 private void extractClientCertificate(HttpServletRequest req) {
210 X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
211 if (null != certs && certs.length > 0) {// Servlet container verified the client certificate
212 String certDn = certs[0].getSubjectX500Principal().getName();
213 sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, certDn);
214 sharedState.put(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN, certs);
215 if (log.isDebugEnabled())
216 log.debug("Client certificate " + certDn + " verified by servlet container");
217 } // Reverse proxy verified the client certificate
218 String clientDnHttpHeader = Activator.getHttpProxySslHeader();
219 if (clientDnHttpHeader != null) {
220 String certDn = req.getHeader(clientDnHttpHeader);
221 // TODO retrieve more cf. https://httpd.apache.org/docs/current/mod/mod_ssl.html
222 // String issuerDn = req.getHeader("SSL_CLIENT_I_DN");
223 if (certDn != null && !certDn.trim().equals("(null)")) {
224 sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, certDn);
225 sharedState.put(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN, "");
226 if (log.isDebugEnabled())
227 log.debug("Client certificate " + certDn + " verified by reverse proxy");
228 }
229 }
230 }
231
232 }