Skip to content

Commit 5435b0a

Browse files
Direct download certificates additions and improvements (#6104)
* Add direct download certificates listing * Restore class to original project * Small refactor * Register API * Apply suggestions from code review Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com> * Refactor after review * Fix checkstyle * Add hosts mapping to API response * Improvements on revoke certificate * Refactor revoke certificate API * Fix condition * Filter only certificates not revoked for revokeCertificate API * Improve upload certificate and add provision certificate API * Improve certificate response output * Address review comments * Refactor revoke cert test * Fix marvin test * Address review comments * Fix issues * Improvements * Refactor upload template API response * Fix response Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>
1 parent 177f048 commit 5435b0a

21 files changed

Lines changed: 950 additions & 106 deletions

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class ApiConstants {
2626
public static final String ADAPTER_TYPE = "adaptertype";
2727
public static final String ADDRESS = "address";
2828
public static final String ALGORITHM = "algorithm";
29+
public static final String ALIAS = "alias";
2930
public static final String ALLOCATED_ONLY = "allocatedonly";
3031
public static final String ANNOTATION = "annotation";
3132
public static final String API_KEY = "apikey";
@@ -58,6 +59,10 @@ public class ApiConstants {
5859
public static final String CERTIFICATE_CHAIN = "certchain";
5960
public static final String CERTIFICATE_FINGERPRINT = "fingerprint";
6061
public static final String CERTIFICATE_ID = "certid";
62+
public static final String CERTIFICATE_ISSUER = "issuer";
63+
public static final String CERTIFICATE_SERIALNUM = "serialnum";
64+
public static final String CERTIFICATE_SUBJECT = "subject";
65+
public static final String CERTIFICATE_VALIDITY = "validity";
6166
public static final String ENABLED_REVOCATION_CHECK = "enabledrevocationcheck";
6267
public static final String CONTROLLER = "controller";
6368
public static final String CONTROLLER_UNIT = "controllerunit";
@@ -188,6 +193,7 @@ public class ApiConstants {
188193
public static final String HOST_ID = "hostid";
189194
public static final String HOST_IDS = "hostids";
190195
public static final String HOST_NAME = "hostname";
196+
public static final String HOSTS_MAP = "hostsmap";
191197
public static final String HYPERVISOR = "hypervisor";
192198
public static final String INLINE = "inline";
193199
public static final String INSTANCE = "instance";
@@ -237,6 +243,7 @@ public class ApiConstants {
237243
public static final String LEVEL = "level";
238244
public static final String LENGTH = "length";
239245
public static final String LIMIT_CPU_USE = "limitcpuuse";
246+
public static final String LIST_HOSTS = "listhosts";
240247
public static final String LOCK = "lock";
241248
public static final String LUN = "lun";
242249
public static final String LBID = "lbruleid";
@@ -322,6 +329,7 @@ public class ApiConstants {
322329
public static final String RESOURCE_TYPE_NAME = "resourcetypename";
323330
public static final String RESPONSE = "response";
324331
public static final String REVERTABLE = "revertable";
332+
public static final String REVOKED = "revoked";
325333
public static final String REGISTERED = "registered";
326334
public static final String QUALIFIERS = "qualifiers";
327335
public static final String QUERY_FILTER = "queryfilter";

api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@
2323
import java.util.Set;
2424

2525
import com.cloud.server.ResourceIcon;
26+
import com.cloud.utils.Pair;
27+
import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse;
2628
import org.apache.cloudstack.api.response.ResourceIconResponse;
29+
import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse;
2730
import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse;
2831
import com.cloud.resource.RollingMaintenanceManager;
2932
import org.apache.cloudstack.api.response.RollingMaintenanceResponse;
33+
import org.apache.cloudstack.direct.download.DirectDownloadCertificate;
34+
import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap;
35+
import org.apache.cloudstack.direct.download.DirectDownloadManager;
3036
import org.apache.cloudstack.management.ManagementServerHost;
3137
import org.apache.cloudstack.affinity.AffinityGroup;
3238
import org.apache.cloudstack.affinity.AffinityGroupResponse;
@@ -491,4 +497,11 @@ List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachine
491497

492498
ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon);
493499

500+
DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate);
501+
502+
List<DirectDownloadCertificateHostStatusResponse> createDirectDownloadCertificateHostMapResponse(List<DirectDownloadCertificateHostMap> hostMappings);
503+
504+
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus status);
505+
506+
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair<Boolean, String> result);
494507
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.admin.direct.download;
18+
19+
import com.cloud.exception.ConcurrentOperationException;
20+
import com.cloud.exception.InsufficientCapacityException;
21+
import com.cloud.exception.NetworkRuleConflictException;
22+
import com.cloud.exception.ResourceAllocationException;
23+
import com.cloud.exception.ResourceUnavailableException;
24+
import org.apache.cloudstack.acl.RoleType;
25+
import org.apache.cloudstack.api.APICommand;
26+
import org.apache.cloudstack.api.ApiConstants;
27+
import org.apache.cloudstack.api.BaseCmd;
28+
import org.apache.cloudstack.api.BaseListCmd;
29+
import org.apache.cloudstack.api.Parameter;
30+
import org.apache.cloudstack.api.ServerApiException;
31+
import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse;
32+
import org.apache.cloudstack.api.response.ListResponse;
33+
import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse;
34+
import org.apache.cloudstack.api.response.ZoneResponse;
35+
import org.apache.cloudstack.context.CallContext;
36+
import org.apache.cloudstack.direct.download.DirectDownloadCertificate;
37+
import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap;
38+
import org.apache.cloudstack.direct.download.DirectDownloadManager;
39+
import org.apache.log4j.Logger;
40+
41+
import javax.inject.Inject;
42+
import java.util.ArrayList;
43+
import java.util.List;
44+
45+
@APICommand(name = ListTemplateDirectDownloadCertificatesCmd.APINAME,
46+
description = "List the uploaded certificates for direct download templates",
47+
responseObject = DirectDownloadCertificateResponse.class,
48+
since = "4.17.0",
49+
authorized = {RoleType.Admin})
50+
public class ListTemplateDirectDownloadCertificatesCmd extends BaseListCmd {
51+
52+
@Inject
53+
DirectDownloadManager directDownloadManager;
54+
55+
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class,
56+
description = "list direct download certificate by ID")
57+
private Long id;
58+
59+
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class,
60+
description = "the zone where certificates are uploaded")
61+
private Long zoneId;
62+
63+
@Parameter(name = ApiConstants.LIST_HOSTS, type = CommandType.BOOLEAN,
64+
description = "if set to true: include the hosts where the certificate is uploaded to")
65+
private Boolean listHosts;
66+
67+
private static final Logger LOG = Logger.getLogger(ListTemplateDirectDownloadCertificatesCmd.class);
68+
public static final String APINAME = "listTemplateDirectDownloadCertificates";
69+
70+
public boolean isListHosts() {
71+
return listHosts != null && listHosts;
72+
}
73+
74+
private void createResponse(final List<DirectDownloadCertificate> certificates) {
75+
final ListResponse<DirectDownloadCertificateResponse> response = new ListResponse<>();
76+
final List<DirectDownloadCertificateResponse> responses = new ArrayList<>();
77+
for (final DirectDownloadCertificate certificate : certificates) {
78+
if (certificate == null) {
79+
continue;
80+
}
81+
DirectDownloadCertificateResponse certificateResponse = _responseGenerator.createDirectDownloadCertificateResponse(certificate);
82+
if (isListHosts()) {
83+
List<DirectDownloadCertificateHostMap> hostMappings = directDownloadManager.getCertificateHostsMapping(certificate.getId());
84+
List<DirectDownloadCertificateHostStatusResponse> hostMapResponses = _responseGenerator.createDirectDownloadCertificateHostMapResponse(hostMappings);
85+
certificateResponse.setHostsMap(hostMapResponses);
86+
}
87+
responses.add(certificateResponse);
88+
}
89+
response.setResponses(responses);
90+
response.setResponseName(getCommandName());
91+
setResponseObject(response);
92+
}
93+
94+
@Override
95+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
96+
ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
97+
List<DirectDownloadCertificate> certificates = directDownloadManager.listDirectDownloadCertificates(id, zoneId);
98+
createResponse(certificates);
99+
}
100+
101+
@Override
102+
public String getCommandName() {
103+
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
104+
}
105+
106+
@Override
107+
public long getEntityOwnerId() {
108+
return CallContext.current().getCallingAccount().getId();
109+
}
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
package org.apache.cloudstack.api.command.admin.direct.download;
20+
21+
import com.cloud.exception.ConcurrentOperationException;
22+
import com.cloud.exception.InsufficientCapacityException;
23+
import com.cloud.exception.NetworkRuleConflictException;
24+
import com.cloud.exception.ResourceAllocationException;
25+
import com.cloud.exception.ResourceUnavailableException;
26+
import com.cloud.utils.Pair;
27+
import org.apache.cloudstack.acl.RoleType;
28+
import org.apache.cloudstack.api.APICommand;
29+
import org.apache.cloudstack.api.ApiConstants;
30+
import org.apache.cloudstack.api.BaseCmd;
31+
import org.apache.cloudstack.api.Parameter;
32+
import org.apache.cloudstack.api.ServerApiException;
33+
import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse;
34+
import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse;
35+
import org.apache.cloudstack.api.response.HostResponse;
36+
import org.apache.cloudstack.context.CallContext;
37+
import org.apache.cloudstack.direct.download.DirectDownloadManager;
38+
39+
import javax.inject.Inject;
40+
41+
@APICommand(name = ProvisionTemplateDirectDownloadCertificateCmd.APINAME,
42+
description = "Provisions a host with a direct download certificate",
43+
responseObject = DirectDownloadCertificateHostStatusResponse.class,
44+
since = "4.17.0",
45+
authorized = {RoleType.Admin})
46+
public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd {
47+
48+
public static final String APINAME = "provisionTemplateDirectDownloadCertificate";
49+
50+
@Inject
51+
DirectDownloadManager directDownloadManager;
52+
53+
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class,
54+
description = "the id of the direct download certificate to provision", required = true)
55+
private Long id;
56+
57+
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class,
58+
description = "the host to provision the certificate", required = true)
59+
private Long hostId;
60+
61+
@Override
62+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
63+
Pair<Boolean, String> result = directDownloadManager.provisionCertificate(id, hostId);
64+
DirectDownloadCertificateHostStatusResponse response = _responseGenerator.createDirectDownloadCertificateProvisionResponse(id, hostId, result);
65+
response.setResponseName(getCommandName());
66+
setResponseObject(response);
67+
}
68+
69+
@Override
70+
public String getCommandName() {
71+
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
72+
}
73+
74+
@Override
75+
public long getEntityOwnerId() {
76+
return CallContext.current().getCallingAccount().getId();
77+
}
78+
}

api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,26 @@
3030
import org.apache.cloudstack.api.BaseCmd;
3131
import org.apache.cloudstack.api.Parameter;
3232
import org.apache.cloudstack.api.ServerApiException;
33+
import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse;
3334
import org.apache.cloudstack.api.response.HostResponse;
34-
import org.apache.cloudstack.api.response.SuccessResponse;
35+
import org.apache.cloudstack.api.response.ListResponse;
36+
import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse;
3537
import org.apache.cloudstack.api.response.ZoneResponse;
3638
import org.apache.cloudstack.context.CallContext;
39+
import org.apache.cloudstack.direct.download.DirectDownloadCertificate;
3740
import org.apache.cloudstack.direct.download.DirectDownloadManager;
41+
import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus;
42+
import org.apache.commons.lang3.ObjectUtils;
43+
import org.apache.commons.lang3.StringUtils;
3844
import org.apache.log4j.Logger;
3945

4046
import javax.inject.Inject;
47+
import java.util.ArrayList;
48+
import java.util.List;
4149

4250
@APICommand(name = RevokeTemplateDirectDownloadCertificateCmd.APINAME,
43-
description = "Revoke a certificate alias from a KVM host",
44-
responseObject = SuccessResponse.class,
45-
requestHasSensitiveInfo = true,
46-
responseHasSensitiveInfo = true,
51+
description = "Revoke a direct download certificate from hosts in a zone",
52+
responseObject = DirectDownloadCertificateHostStatusResponse.class,
4753
since = "4.13",
4854
authorized = {RoleType.Admin})
4955
public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd {
@@ -54,35 +60,63 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd {
5460
private static final Logger LOG = Logger.getLogger(RevokeTemplateDirectDownloadCertificateCmd.class);
5561
public static final String APINAME = "revokeTemplateDirectDownloadCertificate";
5662

57-
@Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, required = true,
58-
description = "alias of the SSL certificate")
63+
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
64+
entityType = DirectDownloadCertificateResponse.class,
65+
description = "id of the certificate")
66+
private Long certificateId;
67+
68+
@Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING,
69+
description = "(optional) alias of the SSL certificate")
5970
private String certificateAlias;
6071

61-
@Parameter(name = ApiConstants.HYPERVISOR, type = BaseCmd.CommandType.STRING, required = true,
62-
description = "hypervisor type")
72+
@Parameter(name = ApiConstants.HYPERVISOR, type = BaseCmd.CommandType.STRING,
73+
description = "(optional) hypervisor type")
6374
private String hypervisor;
6475

6576
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class,
66-
description = "zone to revoke certificate", required = true)
77+
description = "(optional) zone to revoke certificate", required = true)
6778
private Long zoneId;
6879

6980
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class,
7081
description = "(optional) the host ID to revoke certificate")
7182
private Long hostId;
7283

73-
@Override
74-
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
75-
if (!hypervisor.equalsIgnoreCase("kvm")) {
84+
private void createResponse(final List<HostCertificateStatus> hostsRevokeStatusList) {
85+
final ListResponse<DirectDownloadCertificateHostStatusResponse> response = new ListResponse<>();
86+
final List<DirectDownloadCertificateHostStatusResponse> responses = new ArrayList<>();
87+
for (final HostCertificateStatus status : hostsRevokeStatusList) {
88+
if (status == null) {
89+
continue;
90+
}
91+
DirectDownloadCertificateHostStatusResponse revokeResponse =
92+
_responseGenerator.createDirectDownloadCertificateHostStatusResponse(status);
93+
responses.add(revokeResponse);
94+
}
95+
response.setResponses(responses);
96+
response.setResponseName(getCommandName());
97+
setResponseObject(response);
98+
}
99+
100+
private void validateParameters() {
101+
if (ObjectUtils.allNull(certificateId, certificateAlias, hypervisor) ||
102+
certificateId == null && !ObjectUtils.allNotNull(certificateAlias, hypervisor)) {
103+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please specify the hypervisor and the" +
104+
"certificate name to revoke or the certificate ID");
105+
}
106+
if (StringUtils.isNotBlank(hypervisor) && !hypervisor.equalsIgnoreCase("kvm")) {
76107
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Currently supporting KVM hosts only");
77108
}
78-
SuccessResponse response = new SuccessResponse(getCommandName());
109+
}
110+
111+
@Override
112+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
113+
validateParameters();
79114
try {
80-
LOG.debug("Revoking certificate " + certificateAlias + " from " + hypervisor + " hosts");
81-
boolean result = directDownloadManager.revokeCertificateAlias(certificateAlias, hypervisor, zoneId, hostId);
82-
response.setSuccess(result);
83-
setResponseObject(response);
115+
DirectDownloadCertificate certificate = directDownloadManager.findDirectDownloadCertificateByIdOrHypervisorAndAlias(certificateId, certificateAlias, hypervisor, zoneId);
116+
List<HostCertificateStatus> hostsResult = directDownloadManager.revokeCertificate(certificate, zoneId, hostId);
117+
createResponse(hostsResult);
84118
} catch (Exception e) {
85-
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
119+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed revoking certificate: " + e.getMessage());
86120
}
87121
}
88122

0 commit comments

Comments
 (0)