@Override public boolean verifyRequest(Map<String, Object[]> requestParameters, Long userId) throws ServerApiException { try { String apiKey = null; String secretKey = null; String signature = null; String unsignedRequest = null; String[] command = (String[]) requestParameters.get("command"); if (command == null) { s_logger.info("missing command, ignoring request..."); return false; } String commandName = command[0]; // if userId not null, that mean that user is logged in if (userId != null) { User user = ApiDBUtils.findUserById(userId); try { checkCommandAvailable(user, commandName); } catch (RequestLimitException ex) { s_logger.debug(ex.getMessage()); throw new ServerApiException(ApiErrorCode.API_LIMIT_EXCEED, ex.getMessage()); } catch (PermissionDeniedException ex) { s_logger.debug( "The given command:" + commandName + " does not exist or it is not available for user with id:" + userId); throw new ServerApiException( ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user"); } return true; } else { // check against every available command to see if the command exists or not if (!_apiNameCmdClassMap.containsKey(commandName) && !commandName.equals("login") && !commandName.equals("logout")) { s_logger.debug( "The given command:" + commandName + " does not exist or it is not available for user with id:" + userId); throw new ServerApiException( ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user"); } } // - 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); String signatureVersion = null; String expires = null; 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; } else if ("signatureversion".equalsIgnoreCase(paramName)) { signatureVersion = paramValue; } else if ("expires".equalsIgnoreCase(paramName)) { expires = 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)) { s_logger.debug( "Expired session, missing signature, or missing apiKey -- ignoring request. Signature: " + signature + ", apiKey: " + apiKey); return false; // no signature, bad request } Date expiresTS = null; // FIXME: Hard coded signature, why not have an enum if ("3".equals(signatureVersion)) { // New signature authentication. Check for expire parameter and its validity if (expires == null) { s_logger.debug( "Missing Expires parameter -- ignoring request. Signature: " + signature + ", apiKey: " + apiKey); return false; } synchronized (_dateFormat) { try { expiresTS = _dateFormat.parse(expires); } catch (ParseException pe) { s_logger.debug("Incorrect date format for Expires parameter", pe); return false; } } Date now = new Date(System.currentTimeMillis()); if (expiresTS.before(now)) { s_logger.debug( "Request expired -- ignoring ...sig: " + signature + ", apiKey: " + apiKey); return false; } } TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); txn.close(); User user = null; // verify there is a user with this api key Pair<User, Account> userAcctPair = _accountMgr.findUserByApiKey(apiKey); if (userAcctPair == null) { s_logger.debug( "apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey); return false; } user = userAcctPair.first(); Account account = userAcctPair.second(); if (user.getState() != Account.State.enabled || !account.getState().equals(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; } try { checkCommandAvailable(user, commandName); } catch (PermissionDeniedException ex) { s_logger.debug( "The given command:" + commandName + " does not exist or it is not available for user"); throw new ServerApiException( ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist or it is not available for user with id:" + userId); } // 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: "******"HmacSHA1"); SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); mac.init(keySpec); mac.update(unsignedRequest.getBytes()); byte[] encryptedBytes = mac.doFinal(); String computedSignature = Base64.encodeBase64String(encryptedBytes); boolean equalSig = signature.equals(computedSignature); if (!equalSig) { s_logger.info( "User signature: " + signature + " is not equaled to computed signature: " + computedSignature); } else { CallContext.register(user, account); } return equalSig; } catch (ServerApiException ex) { throw ex; } catch (Exception ex) { s_logger.error("unable to verify request signature"); } return false; }