237 lines
7.4 KiB
Plaintext
237 lines
7.4 KiB
Plaintext
package com.hp.csbu.cc.middleware;
|
|
|
|
import java.text.Format;
|
|
import java.text.ParseException;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Date;
|
|
import java.util.Enumeration;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.SimpleTimeZone;
|
|
import java.util.TreeMap;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import javax.servlet.ServletRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
import com.hp.csbu.cc.security.cs.thrift.service.SigAuthRequest;
|
|
import com.hp.csbu.cc.security.cs.thrift.service.SignatureCredentials;
|
|
import static com.hp.csbu.cc.middleware.AuthConstants.SIGNATURE_METHOD;
|
|
|
|
public class HPS3Signer implements Signer {
|
|
|
|
private final Set<String> subResources = new HashSet<String>() {
|
|
{
|
|
add("acl");
|
|
add("logging");
|
|
add("torrent");
|
|
add("location");
|
|
add("requestPayment");
|
|
}
|
|
};
|
|
|
|
public SigAuthRequest sign(ServletRequest req, String serviceIds,
|
|
String endPointIds) {
|
|
String accessKeyId = null;
|
|
String signature = null;
|
|
String dataToSign = null;
|
|
if (req.getParameter("AWSAccessKeyId") != null) {
|
|
if (hasRequestExpired(req)) {
|
|
throw new SignatureBuilderException(
|
|
"Either signature is expired or expiration field is missing");
|
|
}
|
|
req.setAttribute("Date", req.getParameter("Expires"));
|
|
accessKeyId = req.getParameter("AWSAccessKeyId");
|
|
signature = req.getParameter("Signature");
|
|
} else {
|
|
Date d = null;
|
|
if (((HttpServletRequest) req).getHeader("X-Amz-Date") != null) {
|
|
d = parseDate(((HttpServletRequest) req)
|
|
.getHeader("X-Amz-Date"));
|
|
} else if (((HttpServletRequest) req).getHeader("Date") != null) {
|
|
d = parseDate(((HttpServletRequest) req).getHeader("Date"));
|
|
} else {
|
|
throw new SignatureBuilderException(
|
|
"Either date is missing or an invalid date is provided");
|
|
}
|
|
if (hasClockSkew(d)) {
|
|
throw new SignatureBuilderException(
|
|
"Date is invalid. Date is skewed by more than 15 mins");
|
|
}
|
|
String authorizationStr = ((HttpServletRequest) req)
|
|
.getHeader("Authorization");
|
|
authorizationStr = authorizationStr.substring(
|
|
authorizationStr.indexOf(" ")).trim();
|
|
int colonDelimeter = authorizationStr.lastIndexOf(":");
|
|
accessKeyId = authorizationStr.substring(0, colonDelimeter);
|
|
signature = authorizationStr.substring(colonDelimeter + 1);
|
|
}
|
|
dataToSign = getCanonicalString(req);
|
|
return getSignedRequest(dataToSign, accessKeyId, signature,
|
|
SIGNATURE_METHOD, serviceIds, endPointIds);
|
|
}
|
|
|
|
protected boolean hasClockSkew(Date d) {
|
|
if (d == null) {
|
|
// Invalid date
|
|
return true;
|
|
}
|
|
Date currentDate = new Date();
|
|
long currentTime = currentDate.getTime();
|
|
long epochTime = getUnixEpochTime();
|
|
long requestTime = d.getTime();
|
|
long delta = TimeUnit.MILLISECONDS.convert(15L, TimeUnit.MINUTES);
|
|
|
|
if (requestTime < epochTime) {
|
|
// Invalid date
|
|
return true;
|
|
}
|
|
|
|
if (Math.abs(requestTime - currentTime) > delta) {
|
|
// Invalid date
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected String getCanonicalString(ServletRequest req) {
|
|
HttpServletRequest request = ((HttpServletRequest) req);
|
|
StringBuffer canonicalStr = new StringBuffer();
|
|
Map<String, String> amzHeaders = new TreeMap<String, String>();
|
|
String contentMd5 = (request.getHeader("Content-MD5") == null) ? ""
|
|
: request.getHeader("Content-MD5");
|
|
String contentType = (request.getHeader("Content-Type") == null) ? ""
|
|
: request.getHeader("Content-Type");
|
|
String method = request.getMethod();
|
|
canonicalStr.append(method).append("\n").append(contentMd5)
|
|
.append("\n").append(contentType).append("\n");
|
|
|
|
Enumeration<String> headers = request.getHeaderNames();
|
|
while (headers.hasMoreElements()) {
|
|
String headerName = ((String) headers.nextElement()).toLowerCase();
|
|
if (headerName.startsWith("x-amz-")) {
|
|
Enumeration multiHeaderValues = request.getHeaders(headerName);
|
|
StringBuffer headerValues = new StringBuffer();
|
|
headerValues.append((String) multiHeaderValues.nextElement());
|
|
while (multiHeaderValues.hasMoreElements()) {
|
|
headerValues.append(",").append(
|
|
multiHeaderValues.nextElement());
|
|
}
|
|
amzHeaders.put(headerName, headerValues.toString());
|
|
}
|
|
}
|
|
if (amzHeaders.containsKey("X-Amz-Date")) {
|
|
canonicalStr.append("\n");
|
|
} else {
|
|
String date = (request.getHeader("Date") != null) ? (String) request
|
|
.getHeader("Date") : (String) request.getAttribute("Date");
|
|
canonicalStr.append(date).append("\n");
|
|
}
|
|
|
|
for (String key : amzHeaders.keySet()) {
|
|
canonicalStr.append(key).append(":").append(amzHeaders.get(key))
|
|
.append("\n");
|
|
}
|
|
String resource = getCanonicalResource(request);
|
|
canonicalStr.append(resource);
|
|
return canonicalStr.toString();
|
|
}
|
|
|
|
protected String getCanonicalResource(HttpServletRequest request) {
|
|
StringBuffer canonicalResource = new StringBuffer();
|
|
canonicalResource.append(request.getRequestURI());
|
|
String queryString = request.getQueryString();
|
|
if (queryString != null) {
|
|
canonicalResource.append(getCanonicalSubResource(queryString));
|
|
}
|
|
return canonicalResource.toString();
|
|
}
|
|
|
|
protected String getCanonicalSubResource(String queryString) {
|
|
String[] queryParams;
|
|
if (queryString.contains("&")) {
|
|
queryParams = queryString.split("&");
|
|
} else {
|
|
queryParams = new String[1];
|
|
queryParams[0] = queryString;
|
|
}
|
|
for (String param : queryParams) {
|
|
String paramName = param;
|
|
if (paramName.contains("=")) {
|
|
paramName = paramName.substring(0, paramName.indexOf("="));
|
|
}
|
|
if (subResources.contains(paramName)) {
|
|
return "?" + paramName;
|
|
}
|
|
}
|
|
return "";
|
|
|
|
}
|
|
|
|
protected long getUnixEpochTime() {
|
|
Format formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
|
|
Date date;
|
|
try {
|
|
date = (Date) formatter.parseObject("01 Jan 1970 00:00:00 UTC");
|
|
} catch (ParseException e) {
|
|
return 0;
|
|
}
|
|
return date.getTime();
|
|
}
|
|
|
|
protected boolean hasRequestExpired(ServletRequest req) {
|
|
if (req.getParameter("Expires") == null) {
|
|
return true;
|
|
}
|
|
long expirationTime = new Long(req.getParameter("Expires"));
|
|
return expirationTime < (System.currentTimeMillis()/1000);
|
|
}
|
|
|
|
protected Date parseDate(String data) {
|
|
/* Currently date is parsed in RFC 822 format */
|
|
SimpleDateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
|
|
df.setTimeZone(new SimpleTimeZone(0, "UTC"));
|
|
try {
|
|
return df.parse(data);
|
|
} catch (ParseException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
protected SigAuthRequest getSignedRequest(String dataToSign,
|
|
String accessKeyId, String signature, String signatureMethod,
|
|
String serviceIds, String endPointIds) {
|
|
try {
|
|
SignatureCredentials credentials = null;
|
|
Map<String, String> params = new HashMap<String, String>();
|
|
String tenantId = null;
|
|
String keyId = accessKeyId;
|
|
|
|
if (accessKeyId.contains(":")) {
|
|
String[] strArr = accessKeyId.split(":");
|
|
tenantId = strArr[0];
|
|
keyId = strArr[1];
|
|
}
|
|
credentials = new SignatureCredentials(keyId, "accesskey",
|
|
signatureMethod, dataToSign, signature);
|
|
//If tenantId is null, you get an unscoped token.
|
|
if (tenantId != null) {
|
|
params.put("tenantId", tenantId);
|
|
}
|
|
if (serviceIds != null) {
|
|
params.put("serviceIds", serviceIds);
|
|
}
|
|
if (endPointIds != null) {
|
|
params.put("endPointTemplateIds", endPointIds);
|
|
}
|
|
return new SigAuthRequest(credentials, params);
|
|
} catch (Exception e) {
|
|
throw new SignatureBuilderException(
|
|
"Exception building signature with given credentials");
|
|
}
|
|
}
|
|
|
|
}
|