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