diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListSslCertsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListSslCertsCmd.java index 1bc300fdc69f..123339702750 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListSslCertsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListSslCertsCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ProjectResponse; @@ -50,9 +51,15 @@ public class ListSslCertsCmd extends BaseCmd { @Parameter(name = ApiConstants.CERTIFICATE_ID, type = CommandType.UUID, entityType = SslCertResponse.class, required = false, description = "ID of SSL certificate") private Long certId; - @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, required = false, description = "Account ID") + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, required = false, description = "Account ID and " + ApiConstants.ACCOUNT + " are mutually exclusive.") private Long accountId; + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, since = "4.24", description = "Account owning the SSL certificate") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, since = "4.24", entityType = DomainResponse.class, description = "Domain ID of the account owning the SSL certificate") + private Long domainId; + @Parameter(name = ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = false, description = "Load balancer rule ID") private Long lbId; @@ -79,6 +86,14 @@ public Long getProjectId() { return projectId; } + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/server/src/main/java/org/apache/cloudstack/network/ssl/CertServiceImpl.java b/server/src/main/java/org/apache/cloudstack/network/ssl/CertServiceImpl.java index d101ab989caa..a607bb5a68e2 100644 --- a/server/src/main/java/org/apache/cloudstack/network/ssl/CertServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/network/ssl/CertServiceImpl.java @@ -128,6 +128,18 @@ public CertServiceImpl() { public SslCertResponse uploadSslCert(final UploadSslCertCmd certCmd) { Preconditions.checkNotNull(certCmd); + final CallContext ctx = CallContext.current(); + final Account caller = ctx.getCallingAccount(); + + Account owner; + if ((StringUtils.isNotBlank(certCmd.getAccountName()) && certCmd.getDomainId() != null) || certCmd.getProjectId() != null) { + owner = _accountMgr.finalizeOwner(caller, certCmd.getAccountName(), certCmd.getDomainId(), certCmd.getProjectId()); + } else { + owner = caller; + } + + Preconditions.checkNotNull(owner); + final String cert = certCmd.getCert(); final String key = certCmd.getKey(); final String password = certCmd.getPassword(); @@ -138,17 +150,6 @@ public SslCertResponse uploadSslCert(final UploadSslCertCmd certCmd) { logger.debug("Certificate Validation succeeded"); final String fingerPrint = CertificateHelper.generateFingerPrint(parseCertificate(cert)); - - final CallContext ctx = CallContext.current(); - final Account caller = ctx.getCallingAccount(); - - Account owner = null; - if (StringUtils.isNotEmpty(certCmd.getAccountName()) && certCmd.getDomainId() != null || certCmd.getProjectId() != null) { - owner = _accountMgr.finalizeOwner(caller, certCmd.getAccountName(), certCmd.getDomainId(), certCmd.getProjectId()); - } else { - owner = caller; - } - final Long accountId = owner.getId(); final Long domainId = owner.getDomainId(); @@ -199,16 +200,44 @@ public List listSslCerts(final ListSslCertsCmd listSslCertCmd) final Account caller = ctx.getCallingAccount(); final Long certId = listSslCertCmd.getCertId(); - final Long accountId = listSslCertCmd.getAccountId(); final Long lbRuleId = listSslCertCmd.getLbId(); final Long projectId = listSslCertCmd.getProjectId(); + final Long accountId = listSslCertCmd.getAccountId(); + final String accountName = listSslCertCmd.getAccountName(); + final Long domainId = listSslCertCmd.getDomainId(); - final List certResponseList = new ArrayList(); + if (accountId != null && (StringUtils.isNotBlank(accountName) || domainId != null)) { + throw new InvalidParameterValueException("The accountid and account/domainid are mutually exclusive"); + } - if (certId == null && accountId == null && lbRuleId == null && projectId == null) { - throw new InvalidParameterValueException("Invalid parameters either certificate ID or Account ID or Loadbalancer ID or Project ID required"); + // Validate that only one of certid, lbid, projectid, or accountid/account can be specified + ArrayList params = new ArrayList<>(); + params.add(certId); + params.add(accountId != null ? accountId : accountName); + params.add(lbRuleId); + params.add(projectId); + + int nonNullIds = 0; + for (Object param : params) { + if (param != null) { + nonNullIds++; + } + } + if (nonNullIds > 1) { + throw new InvalidParameterValueException("Only one of certid, lbid, projectid, or accountid/account can be specified"); } + Account owner; + if ((StringUtils.isNotBlank(accountName) && domainId != null) || projectId != null) { + owner = _accountMgr.finalizeOwner(caller, accountName, domainId, projectId); + } else { + owner = caller; + } + + Preconditions.checkNotNull(owner); + + final List certResponseList = new ArrayList(); + List certLbMap = null; SslCertVO certVO = null; @@ -241,7 +270,7 @@ public List listSslCerts(final ListSslCertsCmd listSslCertCmd) lbCertMapRule = _lbCertDao.findByLbRuleId(lbRuleId); if (lbCertMapRule == null) { - logger.debug("No certificate bound to loadbalancer id: " + lbRuleId); + logger.debug("No certificate bound to loadbalancer id: {}", lbRuleId); return certResponseList; } @@ -273,8 +302,7 @@ public List listSslCerts(final ListSslCertsCmd listSslCertCmd) return certResponseList; } - //reached here look by accountId - final List certVOList = _sslCertDao.listByAccountId(accountId); + final List certVOList = _sslCertDao.listByAccountId(accountId != null ? accountId : owner.getId()); if (certVOList == null || certVOList.isEmpty()) { return certResponseList; } @@ -374,7 +402,7 @@ private void validateKeys(final PublicKey pubKey, final PrivateKey privKey) { } // No encryption for DSA - if (pubKey.getAlgorithm() != "RSA") { + if (!pubKey.getAlgorithm().equals("RSA")) { return; } diff --git a/server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java b/server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java index 0685167c2a42..759383136792 100644 --- a/server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java +++ b/server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java @@ -32,6 +32,7 @@ import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.TransactionLegacy; import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.ListSslCertsCmd; import org.apache.cloudstack.api.command.user.loadbalancer.UploadSslCertCmd; import org.apache.cloudstack.context.CallContext; import org.bouncycastle.openssl.PKCS8Generator; @@ -819,6 +820,25 @@ public void runDeleteSslCertInvalidId() throws NoSuchFieldException, IllegalAcce } + @Test + public void runListSslCertsUsesCallerAccountWhenNoFilters() { + final long callerAccountId = 42L; + final CertServiceImpl certService = new CertServiceImpl(); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.listByAccountId(anyLong())).thenReturn(new ArrayList<>()); + + final AccountVO callerAccount = new AccountVO("testaccount", 1, "networkdomain", Account.Type.NORMAL, UUID.randomUUID().toString()); + callerAccount.setId(callerAccountId); + final UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.unregister(); + CallContext.register(user, callerAccount); + + certService.listSslCerts(new ListSslCertsCmdExtn()); + + Mockito.verify(certService._sslCertDao).listByAccountId(callerAccountId); + } + public class UploadSslCertCmdExtn extends UploadSslCertCmd { @Override public long getEntityOwnerId() { @@ -833,6 +853,13 @@ public long getEntityOwnerId() { } } + public class ListSslCertsCmdExtn extends ListSslCertsCmd { + @Override + public long getEntityOwnerId() { + return 1; + } + } + private String generateEncryptedPrivateKey(String password) throws NoSuchAlgorithmException, OperatorCreationException, IOException { // Generate RSA key pair KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");