1 package org
.argeo
.cms
.internal
.http
;
3 import java
.io
.IOException
;
5 import java
.util
.StringTokenizer
;
7 import javax
.security
.auth
.callback
.Callback
;
8 import javax
.security
.auth
.callback
.CallbackHandler
;
9 import javax
.security
.auth
.callback
.NameCallback
;
10 import javax
.security
.auth
.callback
.PasswordCallback
;
11 import javax
.security
.auth
.login
.LoginContext
;
12 import javax
.security
.auth
.login
.LoginException
;
13 import javax
.servlet
.http
.HttpServletRequest
;
14 import javax
.servlet
.http
.HttpServletResponse
;
16 import org
.apache
.commons
.codec
.binary
.Base64
;
17 import org
.apache
.commons
.logging
.Log
;
18 import org
.apache
.commons
.logging
.LogFactory
;
19 import org
.argeo
.cms
.CmsException
;
20 import org
.argeo
.cms
.auth
.HttpRequestCallback
;
21 import org
.argeo
.cms
.auth
.HttpRequestCallbackHandler
;
22 import org
.argeo
.node
.NodeConstants
;
23 import org
.ietf
.jgss
.GSSContext
;
24 import org
.ietf
.jgss
.GSSCredential
;
25 import org
.ietf
.jgss
.GSSException
;
26 import org
.ietf
.jgss
.GSSManager
;
27 import org
.ietf
.jgss
.GSSName
;
28 import org
.ietf
.jgss
.Oid
;
29 import org
.osgi
.framework
.BundleContext
;
30 import org
.osgi
.framework
.FrameworkUtil
;
31 import org
.osgi
.service
.http
.HttpContext
;
33 class DataHttpContext
implements HttpContext
{
34 private final static Log log
= LogFactory
.getLog(DataHttpContext
.class);
36 private final BundleContext bc
= FrameworkUtil
.getBundle(getClass()).getBundleContext();
38 // FIXME Make it more unique
39 private String httpAuthRealm
= "Argeo";
42 public boolean handleSecurity(final HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
44 if (log
.isTraceEnabled())
45 HttpUtils
.logRequestHeaders(log
, request
);
48 lc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_USER
, new HttpRequestCallbackHandler(request
, response
));
51 } catch (LoginException e
) {
52 CallbackHandler token
= extractHttpAuth(request
, response
);
55 lc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_USER
, token
);
57 } catch (LoginException e1
) {
58 throw new CmsException("Could not login", e1
);
61 lc
= processUnauthorized(request
, response
);
66 request
.setAttribute(NodeConstants
.LOGIN_CONTEXT_USER
, lc
);
71 public URL
getResource(String name
) {
72 return bc
.getBundle().getResource(name
);
76 public String
getMimeType(String name
) {
80 protected LoginContext
processUnauthorized(HttpServletRequest request
, HttpServletResponse response
) {
83 LoginContext lc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_USER
);
86 } catch (LoginException e1
) {
87 if (log
.isDebugEnabled())
88 log
.error("Cannot log in as anonymous", e1
);
93 protected CallbackHandler
extractHttpAuth(final HttpServletRequest httpRequest
, HttpServletResponse httpResponse
) {
94 String authHeader
= httpRequest
.getHeader(HttpUtils
.HEADER_AUTHORIZATION
);
95 if (authHeader
!= null) {
96 StringTokenizer st
= new StringTokenizer(authHeader
);
97 if (st
.hasMoreTokens()) {
98 String basic
= st
.nextToken();
99 if (basic
.equalsIgnoreCase("Basic")) {
101 // TODO manipulate char[]
102 String credentials
= new String(Base64
.decodeBase64(st
.nextToken()), "UTF-8");
103 // log.debug("Credentials: " + credentials);
104 int p
= credentials
.indexOf(":");
106 final String login
= credentials
.substring(0, p
).trim();
107 final char[] password
= credentials
.substring(p
+ 1).trim().toCharArray();
108 return new CallbackHandler() {
109 public void handle(Callback
[] callbacks
) {
110 for (Callback cb
: callbacks
) {
111 if (cb
instanceof NameCallback
)
112 ((NameCallback
) cb
).setName(login
);
113 else if (cb
instanceof PasswordCallback
)
114 ((PasswordCallback
) cb
).setPassword(password
);
115 else if (cb
instanceof HttpRequestCallback
) {
116 ((HttpRequestCallback
) cb
).setRequest(httpRequest
);
117 ((HttpRequestCallback
) cb
).setResponse(httpResponse
);
123 throw new CmsException("Invalid authentication token");
125 } catch (Exception e
) {
126 throw new CmsException("Couldn't retrieve authentication", e
);
128 } else if (basic
.equalsIgnoreCase("Negotiate")) {
130 String _targetName
= "HTTP/mostar.desktop.argeo.pro";
131 String spnegoToken
= st
.nextToken();
132 byte[] authToken
= Base64
.decodeBase64(spnegoToken
);
133 GSSManager manager
= GSSManager
.getInstance();
135 Oid krb5Oid
= new Oid("1.3.6.1.5.5.2"); // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
136 GSSName gssName
= manager
.createName(_targetName
, null);
137 GSSCredential serverCreds
= manager
.createCredential(gssName
, GSSCredential
.INDEFINITE_LIFETIME
,
138 krb5Oid
, GSSCredential
.ACCEPT_ONLY
);
139 GSSContext gContext
= manager
.createContext(serverCreds
);
141 if (gContext
== null) {
142 log
.debug("SpnegoUserRealm: failed to establish GSSContext");
144 while (!gContext
.isEstablished()) {
145 byte[] outToken
= gContext
.acceptSecContext(authToken
, 0, authToken
.length
);
146 String outTokenStr
= Base64
.encodeBase64String(outToken
);
147 httpResponse
.setHeader("WWW-Authenticate", "Negotiate " + outTokenStr
);
149 if (gContext
.isEstablished()) {
150 String clientName
= gContext
.getSrcName().toString();
151 String role
= clientName
.substring(clientName
.indexOf('@') + 1);
153 log
.debug("SpnegoUserRealm: established a security context");
154 log
.debug("Client Principal is: " + gContext
.getSrcName());
155 log
.debug("Server Principal is: " + gContext
.getTargName());
156 log
.debug("Client Default Role: " + role
);
162 } catch (GSSException gsse
) {
163 log
.warn(gsse
, gsse
);
172 protected void askForWwwAuth(HttpServletRequest request
, HttpServletResponse response
) {
173 response
.setStatus(401);
174 response
.setHeader(HttpUtils
.HEADER_WWW_AUTHENTICATE
, "basic realm=\"" + httpAuthRealm
+ "\"");
177 // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate");
178 // response.setDateHeader("Date", System.currentTimeMillis());
179 // response.setDateHeader("Expires", System.currentTimeMillis() + (24 *
181 // response.setHeader("Accept-Ranges", "bytes");
182 // response.setHeader("Connection", "Keep-Alive");
183 // response.setHeader("Keep-Alive", "timeout=5, max=97");
184 // response.setContentType("text/html; charset=UTF-8");