diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index db9c1d1dafde..bd60409affcc 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -45,10 +45,12 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.serializer.GsonHelper; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserAccountDao; import com.cloud.user.dao.UserDao; +import com.google.gson.reflect.TypeToken; import org.apache.cloudstack.acl.APIChecker; import org.apache.cloudstack.acl.ApiKeyPairManagerImpl; import org.apache.cloudstack.acl.ApiKeyPairPermissionVO; @@ -101,6 +103,7 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.kms.KMSManager; @@ -3274,17 +3277,17 @@ public ListResponse listKeys(ListUserKeysCmd cmd) { List responses = new ArrayList<>(); if (cmd.getKeyId() != null || cmd.getApiKeyFilter() != null) { - fetchOnlyOneKeyPair(responses, cmd); + populateSingleKeyPairResponse(responses, cmd); finalResponse.setResponses(responses); return finalResponse; } - Integer total = fetchMultipleKeyPairs(responses, cmd); - finalResponse.setResponses(responses, total); + populateMultipleKeyPairsResponse(responses, cmd); + finalResponse.setResponses(responses); return finalResponse; } - private void fetchOnlyOneKeyPair(List responses, ListUserKeysCmd cmd) { + private void populateSingleKeyPairResponse(List responses, ListUserKeysCmd cmd) { ApiKeyPair keyPair; if (cmd.getKeyId() != null) { keyPair = _accountService.getKeyPairById(cmd.getKeyId()); @@ -3308,7 +3311,7 @@ private void validateAccessToApiKey(ApiKeyPair keyPair) { _accountService.validateCallingUserHasAccessToDesiredUser(keyPair.getUserId()); } - private Integer fetchMultipleKeyPairs(List responses, ListUserKeysCmd cmd) { + private void populateMultipleKeyPairsResponse(List responses, ListUserKeysCmd cmd) { List users; if (cmd.getUserId() != null) { _accountService.validateCallingUserHasAccessToDesiredUser(cmd.getUserId()); @@ -3325,8 +3328,6 @@ private Integer fetchMultipleKeyPairs(List responses, ListUs addKeypairResponse(keyPair, responses, cmd); removeApiKeyPairIfExpired(keyPair); }); - - return keyPairs.second(); } @Override @@ -3367,24 +3368,29 @@ private Boolean isAccessingKeypairSuperset(ApiKeyPair accessedKeyPair, BaseCmd c @Override public String getAccessingApiKey(BaseCmd cmd) { try { - if (cmd instanceof BaseAsyncCmd && ((BaseAsyncCmd) cmd).getJob().toString().contains("\"signature\"")) { - return parseApiKeyFromAsyncJob((BaseAsyncCmd) cmd); + Map requestPayload = cmd.getFullUrlParams(); + + if (cmd instanceof BaseAsyncCmd && ((BaseAsyncCmd) cmd).getJob() instanceof AsyncJobVO) { + String asyncJobPayload = ((AsyncJobVO) ((BaseAsyncCmd) cmd).getJob()).getCmdInfo(); + requestPayload = GsonHelper.getGson().fromJson(asyncJobPayload, new TypeToken>() {}.getType()); } - boolean accessedByApiKey = cmd.getFullUrlParams().containsKey(ApiConstants.SIGNATURE); - String accessingApiKey = cmd.getFullUrlParams().get("apiKey"); + + boolean accessedByApiKey = requestPayload.keySet().stream().anyMatch(ApiConstants.SIGNATURE::equalsIgnoreCase); if (accessedByApiKey) { - return accessingApiKey; + String apiKey = requestPayload.entrySet().stream() + .filter(e -> ApiConstants.API_KEY.equalsIgnoreCase(e.getKey())) + .map(Map.Entry::getValue).findFirst().orElse(null); + if (apiKey != null) { + logger.info("Request's API key is [{}].", apiKey); + return apiKey; + } } } catch (NullPointerException e) { - logger.info("Accessing API through session."); + logger.warn("Unable to identify request API key due to: {}.", e); } - return null; - } - private String parseApiKeyFromAsyncJob(BaseAsyncCmd cmd) { - String jobString = cmd.getJob().toString(); - int indexOfApiKey = jobString.indexOf("apiKey") + 9; - return jobString.substring(indexOfApiKey, jobString.indexOf("\"", indexOfApiKey)); + logger.info("Request's signature or API key were not identified; assuming it has been authenticated via session."); + return null; } private Boolean isApiKeySupersetOfPermission(List baseKeyPairPermissions, List comparedPermissions) { @@ -3603,6 +3609,14 @@ private ApiKeyPairVO validateAndPersistKeyPairAndPermissions(Account account, Ap permissions.add(new ApiKeyPairPermissionVO(0, rule, rulePermission, ruleDescription)); } + if (permissions.isEmpty() && accessingApiKey != null && doesKeyPairHaveExplicitPermissions(accessingApiKey)) { + logger.debug("No rules were specified for the new API key pair. Since the accessing API key [{}]" + + " has explicit permissions, these permissions will be defined as the rule set for the new pair.", accessingApiKey); + permissions = allPermissions.stream().map(permission -> ( + new ApiKeyPairPermissionVO(0, permission.getRule().getRuleString(), permission.getPermission(), permission.getDescription()) + )).collect(Collectors.toList()); + } + if (!isApiKeySupersetOfPermission(allPermissions, permissions)) { throw new InvalidParameterValueException(String.format("The key pair being created has a bigger set of permissions than the account [%s] " + "that owns it. This is not allowed.", account.getUuid())); @@ -3617,6 +3631,16 @@ private ApiKeyPairVO validateAndPersistKeyPairAndPermissions(Account account, Ap return savedApiKeyPair; } + private boolean doesKeyPairHaveExplicitPermissions(String apiKey) { + ApiKeyPair apiKeyPair = keyPairManager.findByApiKey(apiKey); + if (apiKeyPair == null) { + logger.info("Unable to find API key pair entity with the API key [{}].", apiKey); + return false; + } + + return !apiKeyPairPermissionsDao.findAllByApiKeyPairId(apiKeyPair.getId()).isEmpty(); + } + @Override public List getAllKeypairPermissions(String apiKey) { if (apiKey == null) { diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index b3bc69835ff5..ab76071732a3 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -3683,7 +3683,9 @@ public UserVm destroyVm(DestroyVMCmd cmd, boolean checkExpunge) throws ResourceU if (checkExpunge && expunge) { String jobParamsString = ((AsyncJobVO) cmd.getJob()).getCmdInfo(); HashMap jobParams = GsonHelper.getGson().fromJson(jobParamsString, jobParamsType); - String apiKey = jobParams.get("apiKey"); + String apiKey = jobParams.entrySet().stream() + .filter(e -> ApiConstants.API_KEY.equalsIgnoreCase(e.getKey())) + .map(Map.Entry::getValue).findFirst().orElse(null); checkExpungeVmPermission(ctx.getCallingAccount(), apiKey); }