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