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
;
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
;
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);
29 private Subject subject
= null;
30 private CallbackHandler callbackHandler
= null;
31 private Map
<String
, Object
> sharedState
= null;
33 private RemoteAuthRequest request
= null;
34 private RemoteAuthResponse response
= null;
36 private Authorization authorization
;
37 private Locale locale
;
39 @SuppressWarnings("unchecked")
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
;
49 public boolean login() throws LoginException
{
50 if (callbackHandler
== null)
52 RemoteAuthCallback remoteAuthCallback
= new RemoteAuthCallback();
54 callbackHandler
.handle(new Callback
[] { remoteAuthCallback
});
55 } catch (IOException e
) {
56 throw new LoginException("Cannot handle http callback: " + e
.getMessage());
57 } catch (UnsupportedCallbackException e
) {
60 request
= remoteAuthCallback
.getRequest();
61 if (request
== null) {
62 RemoteAuthSession httpSession
= remoteAuthCallback
.getHttpSession();
63 if (httpSession
== null)
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
);
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();
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
);
98 request
.createSession();
101 sharedState
.put(CmsAuthUtils
.SHARED_STATE_HTTP_REQUEST
, request
);
102 extractHttpAuth(request
);
103 extractClientCertificate(request
);
105 if (authorization
== null) {
106 if (log
.isTraceEnabled())
107 log
.trace("HTTP login: " + false);
110 if (log
.isTraceEnabled())
111 log
.trace("HTTP login: " + true);
112 request
.setAttribute(RemoteAuthRequest
.AUTHORIZATION
, authorization
);
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
));
125 if (authorization
!= null) {
126 // Locale locale = request.getLocale();
127 if (locale
== null && request
!= null)
128 locale
= request
.getLocale();
130 subject
.getPublicCredentials().add(locale
);
131 CmsAuthUtils
.addAuthorization(subject
, authorization
);
132 CmsAuthUtils
.registerSessionAuthorization(request
, subject
, authorization
, locale
);
142 public boolean abort() throws LoginException
{
147 private void cleanUp() {
148 authorization
= null;
153 public boolean logout() throws LoginException
{
158 private void extractHttpAuth(final RemoteAuthRequest httpRequest
) {
159 String authHeader
= httpRequest
.getHeader(HttpHeader
.AUTHORIZATION
.getHeaderName());
160 extractHttpAuth(authHeader
);
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
)) {
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(":");
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
);
181 throw new IllegalStateException("Invalid authentication token");
183 } catch (Exception e
) {
184 throw new IllegalStateException("Couldn't retrieve authentication", e
);
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
);
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);
201 // sharedState.put(CmsAuthUtils.SHARED_STATE_NAME, mail);
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");