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