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