mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user