mirror of https://github.com/apache/cloudstack.git
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:
parent
e077b23d98
commit
53097c67f5
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue