mirror of https://github.com/apache/cloudstack.git
Let console proxy servlet support session-less authentication throught API key signed request
This commit is contained in:
parent
0801da0a0f
commit
08b149a6da
|
|
@ -16,56 +16,81 @@
|
|||
*
|
||||
*/
|
||||
|
||||
package com.cloud.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
package com.cloud.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.api.BaseCmd;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.server.ManagementServer;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.ComponentLocator;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.encoding.Base64;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
|
||||
/**
|
||||
* Thumbnail access : /console?cmd=thumbnail&vm=xxx&w=xxx&h=xxx
|
||||
* Console access : /conosole?cmd=access&vm=xxx
|
||||
* Authentication : /console?cmd=auth&vm=xxx&sid=xxx
|
||||
*/
|
||||
public class ConsoleProxyServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = -5515382620323808168L;
|
||||
public static final Logger s_logger = Logger.getLogger(ConsoleProxyServlet.class.getName());
|
||||
private static final int DEFAULT_THUMBNAIL_WIDTH = 144;
|
||||
private static final int DEFAULT_THUMBNAIL_HEIGHT = 110;
|
||||
|
||||
private final ManagementServer _ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name);
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
||||
doGet(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
|
||||
/**
|
||||
* Thumbnail access : /console?cmd=thumbnail&vm=xxx&w=xxx&h=xxx
|
||||
* Console access : /conosole?cmd=access&vm=xxx
|
||||
* Authentication : /console?cmd=auth&vm=xxx&sid=xxx
|
||||
*/
|
||||
public class ConsoleProxyServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = -5515382620323808168L;
|
||||
public static final Logger s_logger = Logger.getLogger(ConsoleProxyServlet.class.getName());
|
||||
private static final int DEFAULT_THUMBNAIL_WIDTH = 144;
|
||||
private static final int DEFAULT_THUMBNAIL_HEIGHT = 110;
|
||||
|
||||
private final ManagementServer _ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name);
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
||||
doGet(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
try {
|
||||
String userId = null;
|
||||
String account = null;
|
||||
Account accountObj = null;
|
||||
|
||||
Map<String, Object[]> params = new HashMap<String, Object[]>();
|
||||
params.putAll(req.getParameterMap());
|
||||
|
||||
HttpSession session = req.getSession(false);
|
||||
if(session == null) {
|
||||
s_logger.info("Invalid web session, reject console/thumbnail access");
|
||||
sendResponse(resp, "Access denied. You haven't logged in or your web session has timed out");
|
||||
return;
|
||||
if(verifyRequest(params)) {
|
||||
userId = (String)params.get(BaseCmd.Properties.USER_ID.getName())[0];
|
||||
account = (String)params.get(BaseCmd.Properties.ACCOUNT.getName())[0];
|
||||
accountObj = (Account)params.get(BaseCmd.Properties.ACCOUNT_OBJ.getName())[0];
|
||||
} else {
|
||||
s_logger.info("Invalid web session or API key in request, reject console/thumbnail access");
|
||||
sendResponse(resp, "Access denied. Invalid web session or API key in request");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
userId = (String)session.getAttribute(BaseCmd.Properties.USER_ID.getName());
|
||||
account = (String)session.getAttribute(BaseCmd.Properties.ACCOUNT.getName());
|
||||
accountObj = (Account)session.getAttribute(BaseCmd.Properties.ACCOUNT_OBJ.getName());
|
||||
}
|
||||
|
||||
String userId = (String)session.getAttribute("userId");
|
||||
String account = (String)session.getAttribute("account");
|
||||
Account accountObj = (Account)session.getAttribute("accountobj");
|
||||
|
||||
// Do a sanity check here to make sure the user hasn't already been deleted
|
||||
if ((userId == null) || (account == null) || (accountObj == null) || !verifyUser(Long.valueOf(userId))) {
|
||||
|
|
@ -73,202 +98,199 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||
sendResponse(resp, "Access denied. Invalid or inconsistent account is found");
|
||||
return;
|
||||
}
|
||||
|
||||
String cmd = req.getParameter("cmd");
|
||||
if(cmd == null || !isValidCmd(cmd)) {
|
||||
s_logger.info("invalid console servlet command: " + cmd);
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
String vmIdString = req.getParameter("vm");
|
||||
long vmId = 0;
|
||||
try {
|
||||
vmId = Long.parseLong(vmIdString);
|
||||
} catch(NumberFormatException e) {
|
||||
s_logger.info("invalid console servlet command parameter: " + vmIdString);
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!checkSessionPermision(req, vmId)) {
|
||||
sendResponse(resp, "Permission denied");
|
||||
return;
|
||||
}
|
||||
|
||||
if(cmd.equalsIgnoreCase("thumbnail"))
|
||||
handleThumbnailRequest(req, resp, vmId);
|
||||
else if(cmd.equalsIgnoreCase("access"))
|
||||
handleAccessRequest(req, resp, vmId);
|
||||
else
|
||||
handleAuthRequest(req, resp, vmId);
|
||||
|
||||
} catch (Throwable e) {
|
||||
s_logger.error("Unexepected exception in ConsoleProxyServlet", e);
|
||||
sendResponse(resp, "");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleThumbnailRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) {
|
||||
VMInstanceVO vm = _ms.findVMInstanceById(vmId);
|
||||
if(vm == null) {
|
||||
s_logger.warn("VM " + vmId + " does not exist, sending blank response for thumbnail request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
if(vm.getHostId() == null) {
|
||||
s_logger.warn("VM " + vmId + " lost host info, sending blank response for thumbnail request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
HostVO host = _ms.getHostBy(vm.getHostId());
|
||||
if(host == null) {
|
||||
s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for thumbnail request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
String rootUrl = _ms.getConsoleAccessUrlRoot(vmId);
|
||||
if(rootUrl == null) {
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
int w = DEFAULT_THUMBNAIL_WIDTH;
|
||||
int h = DEFAULT_THUMBNAIL_HEIGHT;
|
||||
|
||||
String value = req.getParameter("w");
|
||||
try {
|
||||
w = Integer.parseInt(value);
|
||||
} catch(NumberFormatException e) {
|
||||
}
|
||||
|
||||
value = req.getParameter("h");
|
||||
try {
|
||||
h = Integer.parseInt(value);
|
||||
} catch(NumberFormatException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
resp.sendRedirect(composeThumbnailUrl(rootUrl, vm, host, w, h));
|
||||
} catch (IOException e) {
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Client may already close the connection");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAccessRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) {
|
||||
VMInstanceVO vm = _ms.findVMInstanceById(vmId);
|
||||
if(vm == null) {
|
||||
s_logger.warn("VM " + vmId + " does not exist, sending blank response for console access request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
if(vm.getHostId() == null) {
|
||||
s_logger.warn("VM " + vmId + " lost host info, sending blank response for console access request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
HostVO host = _ms.getHostBy(vm.getHostId());
|
||||
if(host == null) {
|
||||
s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for console access request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
String rootUrl = _ms.getConsoleAccessUrlRoot(vmId);
|
||||
if(rootUrl == null) {
|
||||
sendResponse(resp, "<html><body><p>Console access will be ready in a few minutes. Please try it again later.</p></body></html>");
|
||||
return;
|
||||
}
|
||||
|
||||
String cmd = req.getParameter("cmd");
|
||||
if(cmd == null || !isValidCmd(cmd)) {
|
||||
s_logger.info("invalid console servlet command: " + cmd);
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
String vmIdString = req.getParameter("vm");
|
||||
long vmId = 0;
|
||||
try {
|
||||
vmId = Long.parseLong(vmIdString);
|
||||
} catch(NumberFormatException e) {
|
||||
s_logger.info("invalid console servlet command parameter: " + vmIdString);
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!checkSessionPermision(req, vmId, accountObj)) {
|
||||
sendResponse(resp, "Permission denied");
|
||||
return;
|
||||
}
|
||||
|
||||
if(cmd.equalsIgnoreCase("thumbnail"))
|
||||
handleThumbnailRequest(req, resp, vmId);
|
||||
else if(cmd.equalsIgnoreCase("access"))
|
||||
handleAccessRequest(req, resp, vmId);
|
||||
else
|
||||
handleAuthRequest(req, resp, vmId);
|
||||
|
||||
} catch (Throwable e) {
|
||||
s_logger.error("Unexepected exception in ConsoleProxyServlet", e);
|
||||
sendResponse(resp, "Server Internal Error");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleThumbnailRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) {
|
||||
VMInstanceVO vm = _ms.findVMInstanceById(vmId);
|
||||
if(vm == null) {
|
||||
s_logger.warn("VM " + vmId + " does not exist, sending blank response for thumbnail request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
if(vm.getHostId() == null) {
|
||||
s_logger.warn("VM " + vmId + " lost host info, sending blank response for thumbnail request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
HostVO host = _ms.getHostBy(vm.getHostId());
|
||||
if(host == null) {
|
||||
s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for thumbnail request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
String rootUrl = _ms.getConsoleAccessUrlRoot(vmId);
|
||||
if(rootUrl == null) {
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
int w = DEFAULT_THUMBNAIL_WIDTH;
|
||||
int h = DEFAULT_THUMBNAIL_HEIGHT;
|
||||
|
||||
String value = req.getParameter("w");
|
||||
try {
|
||||
w = Integer.parseInt(value);
|
||||
} catch(NumberFormatException e) {
|
||||
}
|
||||
|
||||
value = req.getParameter("h");
|
||||
try {
|
||||
h = Integer.parseInt(value);
|
||||
} catch(NumberFormatException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
resp.sendRedirect(composeThumbnailUrl(rootUrl, vm, host, w, h));
|
||||
} catch (IOException e) {
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Client may already close the connection");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAccessRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) {
|
||||
VMInstanceVO vm = _ms.findVMInstanceById(vmId);
|
||||
if(vm == null) {
|
||||
s_logger.warn("VM " + vmId + " does not exist, sending blank response for console access request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
if(vm.getHostId() == null) {
|
||||
s_logger.warn("VM " + vmId + " lost host info, sending blank response for console access request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
HostVO host = _ms.getHostBy(vm.getHostId());
|
||||
if(host == null) {
|
||||
s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for console access request");
|
||||
sendResponse(resp, "");
|
||||
return;
|
||||
}
|
||||
|
||||
String rootUrl = _ms.getConsoleAccessUrlRoot(vmId);
|
||||
if(rootUrl == null) {
|
||||
sendResponse(resp, "<html><body><p>Console access will be ready in a few minutes. Please try it again later.</p></body></html>");
|
||||
return;
|
||||
}
|
||||
|
||||
String vmName = vm.getName();
|
||||
if(vmName == null)
|
||||
vmName = vm.getInstanceName();
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("<html><title>").append(vmName).append("</title><frameset><frame src=\"").append(composeConsoleAccessUrl(rootUrl, vm, host));
|
||||
sb.append("\"></frame></frameset></html>");
|
||||
sendResponse(resp, sb.toString());
|
||||
}
|
||||
|
||||
private void handleAuthRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) {
|
||||
|
||||
// TODO authentication channel between console proxy VM and management server needs to be secured,
|
||||
// the data is now being sent through private network, but this is apparently not enough
|
||||
VMInstanceVO vm = _ms.findVMInstanceById(vmId);
|
||||
if(vm == null) {
|
||||
s_logger.warn("VM " + vmId + " does not exist, sending failed response for authentication request from console proxy");
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if(vm.getHostId() == null) {
|
||||
s_logger.warn("VM " + vmId + " lost host info, failed response for authentication request from console proxy");
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
HostVO host = _ms.getHostBy(vm.getHostId());
|
||||
if(host == null) {
|
||||
s_logger.warn("VM " + vmId + "'s host does not exist, sending failed response for authentication request from console proxy");
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
String sid = req.getParameter("sid");
|
||||
if(sid == null || !sid.equals(vm.getVncPassword())) {
|
||||
s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword());
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse(resp, "success");
|
||||
}
|
||||
|
||||
private String composeThumbnailUrl(String rootUrl, VMInstanceVO vm, HostVO host, int w, int h) {
|
||||
StringBuffer sb = new StringBuffer(rootUrl);
|
||||
sb.append("/getscreen?host=").append(host.getPrivateIpAddress());
|
||||
sb.append("&port=").append(_ms.getVncPort(vm));
|
||||
sb.append("&sid=").append(vm.getVncPassword());
|
||||
sb.append("&w=").append(w).append("&h=").append(h);
|
||||
sb.append("&tag=").append(vm.getId());
|
||||
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Compose thumbnail url: " + sb.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String composeConsoleAccessUrl(String rootUrl, VMInstanceVO vm, HostVO host) {
|
||||
StringBuffer sb = new StringBuffer(rootUrl);
|
||||
sb.append("/ajax?host=").append(host.getPrivateIpAddress());
|
||||
sb.append("&port=").append(_ms.getVncPort(vm));
|
||||
sb.append("&sid=").append(vm.getVncPassword());
|
||||
sb.append("&tag=").append(vm.getId());
|
||||
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Compose console url: " + sb.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void sendResponse(HttpServletResponse resp, String content) {
|
||||
try {
|
||||
resp.getWriter().print(content);
|
||||
} catch(IOException e) {
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Client may already close the connection");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSessionPermision(HttpServletRequest req, long vmId) {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("<html><title>").append(vmName).append("</title><frameset><frame src=\"").append(composeConsoleAccessUrl(rootUrl, vm, host));
|
||||
sb.append("\"></frame></frameset></html>");
|
||||
sendResponse(resp, sb.toString());
|
||||
}
|
||||
|
||||
private void handleAuthRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) {
|
||||
|
||||
// TODO authentication channel between console proxy VM and management server needs to be secured,
|
||||
// the data is now being sent through private network, but this is apparently not enough
|
||||
VMInstanceVO vm = _ms.findVMInstanceById(vmId);
|
||||
if(vm == null) {
|
||||
s_logger.warn("VM " + vmId + " does not exist, sending failed response for authentication request from console proxy");
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if(vm.getHostId() == null) {
|
||||
s_logger.warn("VM " + vmId + " lost host info, failed response for authentication request from console proxy");
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
HostVO host = _ms.getHostBy(vm.getHostId());
|
||||
if(host == null) {
|
||||
s_logger.warn("VM " + vmId + "'s host does not exist, sending failed response for authentication request from console proxy");
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
String sid = req.getParameter("sid");
|
||||
if(sid == null || !sid.equals(vm.getVncPassword())) {
|
||||
s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword());
|
||||
sendResponse(resp, "failed");
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse(resp, "success");
|
||||
}
|
||||
|
||||
private String composeThumbnailUrl(String rootUrl, VMInstanceVO vm, HostVO host, int w, int h) {
|
||||
StringBuffer sb = new StringBuffer(rootUrl);
|
||||
sb.append("/getscreen?host=").append(host.getPrivateIpAddress());
|
||||
sb.append("&port=").append(_ms.getVncPort(vm));
|
||||
sb.append("&sid=").append(vm.getVncPassword());
|
||||
sb.append("&w=").append(w).append("&h=").append(h);
|
||||
sb.append("&tag=").append(vm.getId());
|
||||
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Compose thumbnail url: " + sb.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String composeConsoleAccessUrl(String rootUrl, VMInstanceVO vm, HostVO host) {
|
||||
StringBuffer sb = new StringBuffer(rootUrl);
|
||||
sb.append("/ajax?host=").append(host.getPrivateIpAddress());
|
||||
sb.append("&port=").append(_ms.getVncPort(vm));
|
||||
sb.append("&sid=").append(vm.getVncPassword());
|
||||
sb.append("&tag=").append(vm.getId());
|
||||
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Compose console url: " + sb.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void sendResponse(HttpServletResponse resp, String content) {
|
||||
try {
|
||||
resp.getWriter().print(content);
|
||||
} catch(IOException e) {
|
||||
if(s_logger.isInfoEnabled())
|
||||
s_logger.info("Client may already close the connection");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSessionPermision(HttpServletRequest req, long vmId, Account accountObj) {
|
||||
|
||||
HttpSession session = req.getSession(false);
|
||||
Account accountObj = (Account)session.getAttribute("accountobj");
|
||||
|
||||
VMInstanceVO vm = _ms.findVMInstanceById(vmId);
|
||||
UserVmVO userVm;
|
||||
switch(vm.getType())
|
||||
|
|
@ -295,14 +317,14 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidCmd(String cmd) {
|
||||
if(cmd.equalsIgnoreCase("thumbnail") || cmd.equalsIgnoreCase("access") || cmd.equalsIgnoreCase("auth"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidCmd(String cmd) {
|
||||
if(cmd.equalsIgnoreCase("thumbnail") || cmd.equalsIgnoreCase("access") || cmd.equalsIgnoreCase("auth"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean verifyUser(Long userId) {
|
||||
|
|
@ -320,4 +342,106 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// copied and modified from ApiServer.java.
|
||||
// TODO need to replace the whole servlet with a API command
|
||||
private boolean verifyRequest(Map<String, Object[]> requestParameters) {
|
||||
try {
|
||||
String apiKey = null;
|
||||
String secretKey = null;
|
||||
String signature = null;
|
||||
String unsignedRequest = null;
|
||||
|
||||
// - build a request string with sorted params, make sure it's all lowercase
|
||||
// - sign the request, verify the signature is the same
|
||||
List<String> parameterNames = new ArrayList<String>();
|
||||
|
||||
for (Object paramNameObj : requestParameters.keySet()) {
|
||||
parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later
|
||||
}
|
||||
|
||||
Collections.sort(parameterNames);
|
||||
|
||||
for (String paramName : parameterNames) {
|
||||
// parameters come as name/value pairs in the form String/String[]
|
||||
String paramValue = ((String[])requestParameters.get(paramName))[0];
|
||||
|
||||
if ("signature".equalsIgnoreCase(paramName)) {
|
||||
signature = paramValue;
|
||||
} else {
|
||||
if ("apikey".equalsIgnoreCase(paramName)) {
|
||||
apiKey = paramValue;
|
||||
}
|
||||
|
||||
if (unsignedRequest == null) {
|
||||
unsignedRequest = paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20");
|
||||
} else {
|
||||
unsignedRequest = unsignedRequest + "&" + paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if api/secret key are passed to the parameters
|
||||
if ((signature == null) || (apiKey == null)) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.info("expired session, missing signature, or missing apiKey -- ignoring request...sig: " + signature + ", apiKey: " + apiKey);
|
||||
}
|
||||
return false; // no signature, bad request
|
||||
}
|
||||
|
||||
Transaction txn = Transaction.open(Transaction.CLOUD_DB);
|
||||
txn.close();
|
||||
User user = null;
|
||||
// verify there is a user with this api key
|
||||
Pair<User, Account> userAcctPair = _ms.findUserByApiKey(apiKey);
|
||||
if (userAcctPair == null) {
|
||||
s_logger.info("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
user = userAcctPair.first();
|
||||
Account account = userAcctPair.second();
|
||||
|
||||
if (!user.getState().equals(Account.ACCOUNT_STATE_ENABLED) || !account.getState().equals(Account.ACCOUNT_STATE_ENABLED)) {
|
||||
s_logger.info("disabled or locked user accessing the api, userid = " + user.getId() + "; name = " + user.getUsername() + "; state: " + user.getState() + "; accountState: " + account.getState());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) {
|
||||
requestParameters.put(BaseCmd.Properties.USER_ID.getName(), new String[] { user.getId().toString() });
|
||||
requestParameters.put(BaseCmd.Properties.ACCOUNT.getName(), new String[] { account.getAccountName() });
|
||||
requestParameters.put(BaseCmd.Properties.DOMAIN_ID.getName(), new String[] { account.getDomainId().toString() });
|
||||
requestParameters.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new Object[] { account });
|
||||
} else {
|
||||
requestParameters.put(BaseCmd.Properties.USER_ID.getName(), new String[] { user.getId().toString() });
|
||||
requestParameters.put(BaseCmd.Properties.ACCOUNT.getName(), new String[] { account.getAccountName() });
|
||||
requestParameters.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new Object[] { account });
|
||||
}
|
||||
|
||||
// verify secret key exists
|
||||
secretKey = user.getSecretKey();
|
||||
if (secretKey == null) {
|
||||
s_logger.info("User does not have a secret key associated with the account -- ignoring request, username: " + user.getUsername());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsignedRequest = unsignedRequest.toLowerCase();
|
||||
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
|
||||
mac.init(keySpec);
|
||||
mac.update(unsignedRequest.getBytes());
|
||||
byte[] encryptedBytes = mac.doFinal();
|
||||
String computedSignature = Base64.encodeBytes(encryptedBytes);
|
||||
boolean equalSig = signature.equals(computedSignature);
|
||||
if (!equalSig) {
|
||||
s_logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature);
|
||||
}
|
||||
return equalSig;
|
||||
} catch (Exception ex) {
|
||||
s_logger.error("unable to verifty request signature", ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue