1) Fix build problem caused by alex's Refactoring

2) Let console proxy servlet support API key to allow session-less access
This commit is contained in:
Kelven Yang 2010-08-17 15:15:06 -07:00
parent e077b23d98
commit 53097c67f5
5 changed files with 184 additions and 36 deletions

View File

@ -252,25 +252,45 @@
<copy todir="${server.dist.dir}/lib">
<fileset dir="${thirdparty.dir}">
<include name="mysql-connector-java-5.1.7-bin.jar" />
<include name="cglib-nodep-2.2.jar" />
<include name="gson-1.3.jar" />
<include name="log4j-1.2.15.jar" />
<include name="apache-log4j-extras-1.0.jar" />
<include name="ehcache-1.5.0.jar" />
<include name="commons-logging-1.1.1.jar" />
<include name="commons-dbcp-1.2.2.jar" />
<include name="commons-pool-1.4.jar" />
<include name="backport-util-concurrent-3.0.jar" />
<include name="httpcore-4.0.jar" />
<include name="commons-httpclient-3.1.jar" />
<include name="commons-codec-1.4.jar" />
<include name="email.jar" />
<include name="xmlrpc-client-3.1.3.jar" />
<include name="xmlrpc-common-3.1.3.jar" />
<include name="xenserver-5.5.0-1.jar" />
<include name="ws-commons-util-1.0.2.jar" />
<include name="trilead-ssh2-build213.jar" />
<include name="cglib-nodep-2.2.jar" />
<include name="gson-1.3.jar" />
<include name="log4j-1.2.15.jar" />
<include name="apache-log4j-extras-1.0.jar" />
<include name="ehcache-1.5.0.jar" />
<include name="commons-logging-1.1.1.jar" />
<include name="commons-dbcp-1.2.2.jar" />
<include name="commons-pool-1.4.jar" />
<include name="backport-util-concurrent-3.0.jar" />
<include name="httpcore-4.0.jar" />
<include name="commons-httpclient-3.1.jar" />
<include name="commons-codec-1.4.jar" />
<include name="email.jar" />
<include name="xmlrpc-client-3.1.3.jar" />
<include name="xmlrpc-common-3.1.3.jar" />
<include name="xenserver-5.5.0-1.jar" />
<include name="ws-commons-util-1.0.2.jar" />
<include name="trilead-ssh2-build213.jar" />
</fileset>
<fileset dir="${thirdparty.dir}/vmware">
<include name="apputils.jar" />
<include name="vim.jar" />
<include name="vim25.jar" />
</fileset>
<fileset dir="${thirdparty.dir}/vmware/lib">
<include name="activation.jar" />
<include name="axis.jar" />
<include name="jaxen-core.jar" />
<include name="jaxen-jdom.jar" />
<include name="jaxrpc.jar" />
<include name="jdom.jar" />
<include name="mailapi.jar" />
<include name="saxpath.jar" />
<include name="smtp.jar" />
<include name="wbem.jar" />
<include name="xalan.jar" />
<include name="xerces.jar" />
<include name="xml-apis.jar" />
</fileset>
</copy>
<copy overwrite="true" todir="${server.dist.dir}/conf">

View File

@ -41,7 +41,7 @@ import com.google.gson.annotations.Expose;
public class VolumeVO implements Volume {
@Id
@TableGenerator(name="volume_sq", table="sequence", pkColumnName="name", valueColumnName="value", pkColumnValue="volume_seq", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@GeneratedValue(strategy=GenerationType.TABLE)
@Column(name="id")
long id;

View File

@ -19,7 +19,15 @@
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;
@ -32,7 +40,10 @@ 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;
@ -57,16 +68,29 @@ public class ConsoleProxyServlet extends HttpServlet {
@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(BaseCmd.Properties.USER_ID.getName());
String account = (String)session.getAttribute(BaseCmd.Properties.ACCOUNT.getName());
Account accountObj = (Account)session.getAttribute(BaseCmd.Properties.ACCOUNT_OBJ.getName());
// 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))) {
@ -92,7 +116,7 @@ public class ConsoleProxyServlet extends HttpServlet {
return;
}
if(!checkSessionPermision(req, vmId)) {
if(!checkSessionPermision(req, vmId, accountObj)) {
sendResponse(resp, "Permission denied");
return;
}
@ -106,7 +130,7 @@ public class ConsoleProxyServlet extends HttpServlet {
} catch (Throwable e) {
s_logger.error("Unexepected exception in ConsoleProxyServlet", e);
sendResponse(resp, "");
sendResponse(resp, "Server Internal Error");
}
}
@ -265,11 +289,8 @@ public class ConsoleProxyServlet extends HttpServlet {
}
}
private boolean checkSessionPermision(HttpServletRequest req, long vmId) {
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())
@ -321,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;
}
}

View File

@ -52,7 +52,7 @@ public class AccountManagerImpl implements AccountManager {
@Inject private VMTemplateDao _templateDao;
@Inject private ResourceLimitDao _resourceLimitDao;
@Inject private ResourceCountDao _resourceCountDao;
@Inject private final GlobalLock m_resourceCountLock = GlobalLock.getInternLock("resource.count");
private final GlobalLock m_resourceCountLock = GlobalLock.getInternLock("resource.count");
AccountVO _systemAccount;

View File

@ -36,9 +36,14 @@ public class MauriceMoss implements VmManager {
@Inject private NetworkManager _networkMgr;
@Override
public VMInstanceVO allocate(VMInstanceVO vm, ServiceOfferingVO serviceOffering, List<NetworkOfferingVO> networkOfferings, List<DiskOfferingVO> diskOffering, DataCenterVO dc, AccountVO account) {
_storageMgr.allocateTemplatedVm(vm, template, rootOffering, dataOffering, size, dc, account)
return null;
public VMInstanceVO allocate(VMInstanceVO vm,
ServiceOfferingVO serviceOffering,
NetworkOfferingVO[] networkOfferings,
DiskOfferingVO[] diskOffering,
DataCenterVO dc,
AccountVO account) {
return null;
}
@Override