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