1 package org
.argeo
.cms
.auth
;
3 import java
.io
.IOException
;
4 import java
.security
.cert
.X509Certificate
;
5 import java
.util
.Base64
;
6 import java
.util
.Locale
;
8 import java
.util
.StringTokenizer
;
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 import javax
.servlet
.http
.HttpServletRequest
;
17 import javax
.servlet
.http
.HttpServletResponse
;
18 import javax
.servlet
.http
.HttpSession
;
20 import org
.apache
.commons
.logging
.Log
;
21 import org
.apache
.commons
.logging
.LogFactory
;
22 import org
.argeo
.cms
.internal
.kernel
.Activator
;
23 import org
.osgi
.framework
.BundleContext
;
24 import org
.osgi
.framework
.FrameworkUtil
;
25 import org
.osgi
.service
.http
.HttpContext
;
26 import org
.osgi
.service
.useradmin
.Authorization
;
28 /** Use the HTTP session as the basis for authentication. */
29 public class HttpSessionLoginModule
implements LoginModule
{
30 private final static Log log
= LogFactory
.getLog(HttpSessionLoginModule
.class);
32 private Subject subject
= null;
33 private CallbackHandler callbackHandler
= null;
34 private Map
<String
, Object
> sharedState
= null;
36 private HttpServletRequest request
= null;
37 private HttpServletResponse response
= null;
39 private BundleContext bc
;
41 private Authorization authorization
;
42 private Locale locale
;
44 @SuppressWarnings("unchecked")
46 public void initialize(Subject subject
, CallbackHandler callbackHandler
, Map
<String
, ?
> sharedState
,
47 Map
<String
, ?
> options
) {
48 bc
= FrameworkUtil
.getBundle(HttpSessionLoginModule
.class).getBundleContext();
50 this.subject
= subject
;
51 this.callbackHandler
= callbackHandler
;
52 this.sharedState
= (Map
<String
, Object
>) sharedState
;
56 public boolean login() throws LoginException
{
57 if (callbackHandler
== null)
59 HttpRequestCallback httpCallback
= new HttpRequestCallback();
61 callbackHandler
.handle(new Callback
[] { httpCallback
});
62 } catch (IOException e
) {
63 throw new LoginException("Cannot handle http callback: " + e
.getMessage());
64 } catch (UnsupportedCallbackException e
) {
67 request
= httpCallback
.getRequest();
68 if (request
== null) {
69 HttpSession httpSession
= httpCallback
.getHttpSession();
70 if (httpSession
== null)
72 // TODO factorize with below
73 String httpSessionId
= httpSession
.getId();
74 if (log
.isTraceEnabled())
75 log
.trace("HTTP login: " + request
.getPathInfo() + " #" + httpSessionId
);
76 CmsSession cmsSession
= CmsAuthUtils
.cmsSessionFromHttpSession(bc
, httpSessionId
);
77 if (cmsSession
!= null) {
78 authorization
= cmsSession
.getAuthorization();
79 locale
= cmsSession
.getLocale();
80 if (log
.isTraceEnabled())
81 log
.trace("Retrieved authorization from " + cmsSession
);
84 authorization
= (Authorization
) request
.getAttribute(HttpContext
.AUTHORIZATION
);
85 if (authorization
== null) {// search by session ID
86 HttpSession httpSession
= request
.getSession(false);
87 if (httpSession
== null) {
88 // TODO make sure this is always safe
89 if (log
.isTraceEnabled())
90 log
.trace("Create http session");
91 httpSession
= request
.getSession(true);
93 String httpSessionId
= httpSession
.getId();
94 if (log
.isTraceEnabled())
95 log
.trace("HTTP login: " + request
.getPathInfo() + " #" + httpSessionId
);
96 CmsSession cmsSession
= CmsAuthUtils
.cmsSessionFromHttpSession(bc
, httpSessionId
);
97 if (cmsSession
!= null) {
98 authorization
= cmsSession
.getAuthorization();
99 locale
= cmsSession
.getLocale();
100 if (log
.isTraceEnabled())
101 log
.trace("Retrieved authorization from " + cmsSession
);
104 sharedState
.put(CmsAuthUtils
.SHARED_STATE_HTTP_REQUEST
, request
);
105 extractHttpAuth(request
);
106 extractClientCertificate(request
);
108 if (authorization
== null) {
109 if (log
.isTraceEnabled())
110 log
.trace("HTTP login: " + false);
113 if (log
.isTraceEnabled())
114 log
.trace("HTTP login: " + true);
115 request
.setAttribute(HttpContext
.AUTHORIZATION
, authorization
);
121 public boolean commit() throws LoginException
{
122 byte[] outToken
= (byte[]) sharedState
.get(CmsAuthUtils
.SHARED_STATE_SPNEGO_OUT_TOKEN
);
123 if (outToken
!= null) {
124 response
.setHeader(CmsAuthUtils
.HEADER_WWW_AUTHENTICATE
,
125 "Negotiate " + java
.util
.Base64
.getEncoder().encodeToString(outToken
));
128 if (authorization
!= null) {
129 // Locale locale = request.getLocale();
130 if (locale
== null && request
!= null)
131 locale
= request
.getLocale();
133 subject
.getPublicCredentials().add(locale
);
134 CmsAuthUtils
.addAuthorization(subject
, authorization
);
135 CmsAuthUtils
.registerSessionAuthorization(request
, subject
, authorization
, locale
);
145 public boolean abort() throws LoginException
{
150 private void cleanUp() {
151 authorization
= null;
156 public boolean logout() throws LoginException
{
161 private void extractHttpAuth(final HttpServletRequest httpRequest
) {
162 String authHeader
= httpRequest
.getHeader(CmsAuthUtils
.HEADER_AUTHORIZATION
);
163 extractHttpAuth(authHeader
);
166 private void extractHttpAuth(String authHeader
) {
167 if (authHeader
!= null) {
168 StringTokenizer st
= new StringTokenizer(authHeader
);
169 if (st
.hasMoreTokens()) {
170 String basic
= st
.nextToken();
171 if (basic
.equalsIgnoreCase("Basic")) {
173 // TODO manipulate char[]
174 Base64
.Decoder decoder
= Base64
.getDecoder();
175 String credentials
= new String(decoder
.decode(st
.nextToken()), "UTF-8");
176 // log.debug("Credentials: " + credentials);
177 int p
= credentials
.indexOf(":");
179 final String login
= credentials
.substring(0, p
).trim();
180 final char[] password
= credentials
.substring(p
+ 1).trim().toCharArray();
181 sharedState
.put(CmsAuthUtils
.SHARED_STATE_NAME
, login
);
182 sharedState
.put(CmsAuthUtils
.SHARED_STATE_PWD
, password
);
184 throw new IllegalStateException("Invalid authentication token");
186 } catch (Exception e
) {
187 throw new IllegalStateException("Couldn't retrieve authentication", e
);
189 } else if (basic
.equalsIgnoreCase("Negotiate")) {
190 String spnegoToken
= st
.nextToken();
191 Base64
.Decoder decoder
= Base64
.getDecoder();
192 byte[] authToken
= decoder
.decode(spnegoToken
);
193 sharedState
.put(CmsAuthUtils
.SHARED_STATE_SPNEGO_TOKEN
, authToken
);
199 // String mail = request.getParameter(LdapAttrs.mail.name());
200 // String authPassword = request.getParameter(LdapAttrs.authPassword.name());
201 // if (authPassword != null) {
202 // sharedState.put(CmsAuthUtils.SHARED_STATE_PWD, authPassword);
204 // sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, mail);
208 private void extractClientCertificate(HttpServletRequest req
) {
209 X509Certificate
[] certs
= (X509Certificate
[]) req
.getAttribute("javax.servlet.request.X509Certificate");
210 if (null != certs
&& certs
.length
> 0) {// Servlet container verified the client certificate
211 String certDn
= certs
[0].getSubjectX500Principal().getName();
212 sharedState
.put(CmsAuthUtils
.SHARED_STATE_NAME
, certDn
);
213 sharedState
.put(CmsAuthUtils
.SHARED_STATE_CERTIFICATE_CHAIN
, certs
);
214 if (log
.isDebugEnabled())
215 log
.debug("Client certificate " + certDn
+ " verified by servlet container");
216 } // Reverse proxy verified the client certificate
217 String clientDnHttpHeader
= Activator
.getHttpProxySslHeader();
218 if (clientDnHttpHeader
!= null) {
219 String certDn
= req
.getHeader(clientDnHttpHeader
);
220 // TODO retrieve more cf. https://httpd.apache.org/docs/current/mod/mod_ssl.html
221 // String issuerDn = req.getHeader("SSL_CLIENT_I_DN");
222 if (certDn
!= null && !certDn
.trim().equals("(null)")) {
223 sharedState
.put(CmsAuthUtils
.SHARED_STATE_NAME
, certDn
);
224 sharedState
.put(CmsAuthUtils
.SHARED_STATE_CERTIFICATE_CHAIN
, "");
225 if (log
.isDebugEnabled())
226 log
.debug("Client certificate " + certDn
+ " verified by reverse proxy");