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