]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/auth/RemoteSessionLoginModule.java
Use runtime namespace context as default.
[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.CmsContextImpl;
21 import org.argeo.cms.internal.runtime.KernelUtils;
22 import org.osgi.service.http.HttpContext;
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 BundleContext bc;
37
38 private Authorization authorization;
39 private Locale locale;
40
41 @SuppressWarnings("unchecked")
42 @Override
43 public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
44 Map<String, ?> options) {
45 // bc = FrameworkUtil.getBundle(RemoteSessionLoginModule.class).getBundleContext();
46 // assert bc != null;
47 this.subject = subject;
48 this.callbackHandler = callbackHandler;
49 this.sharedState = (Map<String, Object>) sharedState;
50 }
51
52 @Override
53 public boolean login() throws LoginException {
54 if (callbackHandler == null)
55 return false;
56 RemoteAuthCallback httpCallback = new RemoteAuthCallback();
57 try {
58 callbackHandler.handle(new Callback[] { httpCallback });
59 } catch (IOException e) {
60 throw new LoginException("Cannot handle http callback: " + e.getMessage());
61 } catch (UnsupportedCallbackException e) {
62 return false;
63 }
64 request = httpCallback.getRequest();
65 if (request == null) {
66 RemoteAuthSession httpSession = httpCallback.getHttpSession();
67 if (httpSession == null)
68 return false;
69 // TODO factorize with below
70 String httpSessionId = httpSession.getId();
71 // if (log.isTraceEnabled())
72 // log.trace("HTTP login: " + request.getPathInfo() + " #" + httpSessionId);
73 CmsSessionImpl cmsSession = CmsContextImpl.getCmsContext().getCmsSessionByLocalId(httpSessionId);
74 if (cmsSession != null && !cmsSession.isAnonymous()) {
75 authorization = cmsSession.getAuthorization();
76 locale = cmsSession.getLocale();
77 if (log.isTraceEnabled())
78 log.trace("Retrieved authorization from " + cmsSession);
79 }
80 } else {
81 authorization = (Authorization) request.getAttribute(HttpContext.AUTHORIZATION);
82 if (authorization == null) {// search by session ID
83 RemoteAuthSession httpSession = request.getSession();
84 if (httpSession == null) {
85 // TODO make sure this is always safe
86 if (log.isTraceEnabled())
87 log.trace("Create http session");
88 httpSession = request.createSession();
89 }
90 String httpSessionId = httpSession.getId();
91 // if (log.isTraceEnabled())
92 // log.trace("HTTP login: " + request.getPathInfo() + " #" + httpSessionId);
93 CmsSessionImpl cmsSession = CmsContextImpl.getCmsContext().getCmsSessionByLocalId(httpSessionId);
94 if (cmsSession != null && !cmsSession.isAnonymous()) {
95 authorization = cmsSession.getAuthorization();
96 locale = cmsSession.getLocale();
97 if (log.isTraceEnabled())
98 log.trace("Retrieved authorization from " + cmsSession);
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(HttpContext.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(CmsAuthUtils.HEADER_WWW_AUTHENTICATE,
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(CmsAuthUtils.HEADER_AUTHORIZATION);
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("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("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 = KernelUtils.getFrameworkProp(CmsConstants.HTTP_PROXY_SSL_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 }