]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java
6bf3fc985a8d1040edfbab0692a4a4e8cd991dae
[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 }
98 }
99 sharedState.put(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST, request);
100 extractHttpAuth(request);
101 extractClientCertificate(request);
102 }
103 if (authorization == null) {
104 if (log.isTraceEnabled())
105 log.trace("HTTP login: " + false);
106 return false;
107 } else {
108 if (log.isTraceEnabled())
109 log.trace("HTTP login: " + true);
110 request.setAttribute(RemoteAuthRequest.AUTHORIZATION, authorization);
111 return true;
112 }
113 }
114
115 @Override
116 public boolean commit() throws LoginException {
117 byte[] outToken = (byte[]) sharedState.get(CmsAuthUtils.SHARED_STATE_SPNEGO_OUT_TOKEN);
118 if (outToken != null) {
119 response.setHeader(HttpHeader.WWW_AUTHENTICATE.getName(),
120 "Negotiate " + java.util.Base64.getEncoder().encodeToString(outToken));
121 }
122
123 if (authorization != null) {
124 // Locale locale = request.getLocale();
125 if (locale == null && request != null)
126 locale = request.getLocale();
127 if (locale != null)
128 subject.getPublicCredentials().add(locale);
129 CmsAuthUtils.addAuthorization(subject, authorization);
130 CmsAuthUtils.registerSessionAuthorization(request, subject, authorization, locale);
131 cleanUp();
132 return true;
133 } else {
134 cleanUp();
135 return false;
136 }
137 }
138
139 @Override
140 public boolean abort() throws LoginException {
141 cleanUp();
142 return false;
143 }
144
145 private void cleanUp() {
146 authorization = null;
147 request = null;
148 }
149
150 @Override
151 public boolean logout() throws LoginException {
152 cleanUp();
153 return true;
154 }
155
156 private void extractHttpAuth(final RemoteAuthRequest httpRequest) {
157 String authHeader = httpRequest.getHeader(HttpHeader.AUTHORIZATION.getName());
158 extractHttpAuth(authHeader);
159 }
160
161 private void extractHttpAuth(String authHeader) {
162 if (authHeader != null) {
163 StringTokenizer st = new StringTokenizer(authHeader);
164 if (st.hasMoreTokens()) {
165 String basic = st.nextToken();
166 if (basic.equalsIgnoreCase(HttpHeader.BASIC)) {
167 try {
168 // TODO manipulate char[]
169 Base64.Decoder decoder = Base64.getDecoder();
170 String credentials = new String(decoder.decode(st.nextToken()), "UTF-8");
171 // log.debug("Credentials: " + credentials);
172 int p = credentials.indexOf(":");
173 if (p != -1) {
174 final String login = credentials.substring(0, p).trim();
175 final char[] password = credentials.substring(p + 1).trim().toCharArray();
176 sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, login);
177 sharedState.put(CmsAuthUtils.SHARED_STATE_PWD, password);
178 } else {
179 throw new IllegalStateException("Invalid authentication token");
180 }
181 } catch (Exception e) {
182 throw new IllegalStateException("Couldn't retrieve authentication", e);
183 }
184 } else if (basic.equalsIgnoreCase(HttpHeader.NEGOTIATE)) {
185 String spnegoToken = st.nextToken();
186 Base64.Decoder decoder = Base64.getDecoder();
187 byte[] authToken = decoder.decode(spnegoToken);
188 sharedState.put(CmsAuthUtils.SHARED_STATE_SPNEGO_TOKEN, authToken);
189 }
190 }
191 }
192
193 // auth token
194 // String mail = request.getParameter(LdapAttrs.mail.name());
195 // String authPassword = request.getParameter(LdapAttrs.authPassword.name());
196 // if (authPassword != null) {
197 // sharedState.put(CmsAuthUtils.SHARED_STATE_PWD, authPassword);
198 // if (mail != null)
199 // sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, mail);
200 // }
201 }
202
203 private void extractClientCertificate(RemoteAuthRequest req) {
204 X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
205 if (null != certs && certs.length > 0) {// Servlet container verified the client certificate
206 String certDn = certs[0].getSubjectX500Principal().getName();
207 sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, certDn);
208 sharedState.put(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN, certs);
209 if (log.isDebugEnabled())
210 log.debug("Client certificate " + certDn + " verified by servlet container");
211 } // Reverse proxy verified the client certificate
212 String clientDnHttpHeader = CmsStateImpl.getDeployProperty(CmsContextImpl.getCmsContext().getCmsState(),
213 CmsDeployProperty.HTTP_PROXY_SSL_HEADER_DN);
214 if (clientDnHttpHeader != null) {
215 String certDn = req.getHeader(clientDnHttpHeader);
216 // TODO retrieve more cf. https://httpd.apache.org/docs/current/mod/mod_ssl.html
217 // String issuerDn = req.getHeader("SSL_CLIENT_I_DN");
218 if (certDn != null && !certDn.trim().equals("(null)")) {
219 sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, certDn);
220 sharedState.put(CmsAuthUtils.SHARED_STATE_CERTIFICATE_CHAIN, "");
221 if (log.isDebugEnabled())
222 log.debug("Client certificate " + certDn + " verified by reverse proxy");
223 }
224 }
225 }
226
227 }