From c64cf81db342261c1498e3cd6ffaf64bf6ed6846 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Fri, 20 Feb 2026 12:45:21 +0530 Subject: [PATCH] include: 1. port for dns_server 2. remove hard coded localhost for server_id 3. resolve and store server id if not passed in the api request 4. restrict public dns_server for domain admins and admins --- .../apache/cloudstack/api/ApiConstants.java | 2 + .../api/command/user/dns/AddDnsServerCmd.java | 17 ++- .../api/response/DnsServerResponse.java | 6 +- .../api/response/DnsZoneResponse.java | 8 -- .../apache/cloudstack/dns/DnsProvider.java | 2 + .../org/apache/cloudstack/dns/DnsServer.java | 6 + .../META-INF/db/schema-42210to42300.sql | 2 + .../dns/powerdns/PowerDnsClient.java | 105 +++++++++++++----- .../dns/powerdns/PowerDnsProvider.java | 85 +++++++++----- .../dns/DnsProviderManagerImpl.java | 23 +++- .../apache/cloudstack/dns/vo/DnsServerVO.java | 34 +++++- 11 files changed, 210 insertions(+), 80 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 605ff28d50e..13dd0305a00 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1335,6 +1335,7 @@ public class ApiConstants { // DNS provider related public static final String NAME_SERVERS = "nameservers"; + public static final String DNS_USER_NAME = "dnsusername"; public static final String CREDENTIALS = "credentials"; public static final String DNS_ZONE_ID = "dnszoneid"; public static final String DNS_SERVER_ID = "dnsserverid"; @@ -1351,6 +1352,7 @@ public class ApiConstants { public static final String X_API_KEY = "X-API-Key"; public static final String DISABLED = "disabled"; public static final String CONTENT_TYPE = "Content-Type"; + public static final String NATIVE_ZONE = "Native"; public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota tariff's activation rule. It can receive a JS script that results in either " + diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java index 126aafba642..7f2148d014e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java @@ -53,7 +53,11 @@ public class AddDnsServerCmd extends BaseCmd { @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = true, description = "Provider type (e.g., PowerDNS)") private String provider; - @Parameter(name = ApiConstants.CREDENTIALS, type = CommandType.STRING, description = "API Key or Credentials for the external provider") + @Parameter(name = ApiConstants.DNS_USER_NAME, type = CommandType.STRING, + description = "Username or email associated with the external DNS provider account (used for authentication)") + private String dnsUserName; + + @Parameter(name = ApiConstants.CREDENTIALS, required = true, type = CommandType.STRING, description = "API key or credentials for the external provider") private String credentials; @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, description = "Port number of the external DNS server") @@ -69,6 +73,9 @@ public class AddDnsServerCmd extends BaseCmd { required = true, description = "Comma separated list of name servers") private List nameServers; + @Parameter(name = "externalserverid", type = CommandType.STRING, description = "External server id or hostname for the DNS server, e.g., 'localhost' for PowerDNS") + private String externalServerId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -117,4 +124,12 @@ public class AddDnsServerCmd extends BaseCmd { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); } } + + public String getExternalServerId() { + return externalServerId; + } + + public String getDnsUserName() { + return dnsUserName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsServerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsServerResponse.java index 57f6ce28d8f..f96e05820f4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DnsServerResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsServerResponse.java @@ -55,10 +55,12 @@ public class DnsServerResponse extends BaseResponse { @Param(description = "Is the DNS server publicly available") private Boolean isPublic; - @SerializedName(ApiConstants.PUBLIC_DOMAIN_SUFFIX) @Param(description = "The public domain suffix for the DNS server") + @SerializedName(ApiConstants.PUBLIC_DOMAIN_SUFFIX) + @Param(description = "The public domain suffix for the DNS server") private String publicDomainSuffix; - @SerializedName(ApiConstants.NAME_SERVERS) @Param(description = "Name servers entries associated to DNS server") + @SerializedName(ApiConstants.NAME_SERVERS) + @Param(description = "Name servers entries associated to DNS server") private List nameServers; public DnsServerResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java index a2b19d32fa1..4bad0b513ea 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java @@ -39,10 +39,6 @@ public class DnsZoneResponse extends BaseResponse { @Param(description = "ID of the DNS server this zone belongs to") private Long dnsServerId; - @SerializedName("dnsservername") - @Param(description = "Name of the DNS server this zone belongs to") - private String dnsServerName; - @SerializedName(ApiConstants.NETWORK_ID) @Param(description = "ID of the network this zone is associated with") private String networkId; @@ -76,10 +72,6 @@ public class DnsZoneResponse extends BaseResponse { this.dnsServerId = dnsServerId; } - public void setDnsServerName(String dnsServerName) { - this.dnsServerName = dnsServerName; - } - public void setNetworkId(String networkId) { this.networkId = networkId; } diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java b/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java index 7d4ab1133b7..852df8dd0bf 100644 --- a/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java @@ -29,6 +29,8 @@ public interface DnsProvider extends Adapter { // Validates connectivity to the server void validate(DnsServer server) throws Exception; + String validateAndResolveServer(DnsServer server) throws Exception; + // Zone Operations String provisionZone(DnsServer server, DnsZone zone) throws DnsProviderException; void deleteZone(DnsServer server, DnsZone zone) throws DnsProviderException; diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java index 14160cdf1ff..49f7ddea5b4 100644 --- a/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java @@ -46,4 +46,10 @@ public interface DnsServer extends InternalIdentity, Identity, ControlledEntity Date getCreated(); Date getRemoved(); + + String getPublicDomainSuffix(); + + String getExternalServerId(); + + Integer getPort(); } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql index 208991bf4b3..244930676c1 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql @@ -62,7 +62,9 @@ CREATE TABLE `cloud`.`dns_server` ( `name` varchar(255) NOT NULL COMMENT 'display name of the dns server', `provider_type` varchar(255) NOT NULL COMMENT 'Provider type such as PowerDns', `url` varchar(1024) NOT NULL COMMENT 'dns server url', + `dns_username` varchar(255) NOT NULL COMMENT 'username or email for dns server credentials', `api_key` varchar(255) NOT NULL COMMENT 'dns server api_key', + `dns_server_name` varchar(255) COMMENT 'dns server name e.g. localhost for powerdns', `port` int(11) DEFAULT NULL COMMENT 'optional dns server port', `name_servers` varchar(1024) DEFAULT NULL COMMENT 'Comma separated list of name servers', `is_public` tinyint(1) NOT NULL DEFAULT '0', diff --git a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java index 28be4f5155e..2a19c306a08 100644 --- a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java +++ b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java @@ -18,6 +18,8 @@ package org.apache.cloudstack.dns.powerdns; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; @@ -62,7 +64,7 @@ public class PowerDnsClient implements AutoCloseable { private static final int MAX_CONNECTIONS_TOTAL = 50; private static final int MAX_CONNECTIONS_PER_ROUTE = 10; private static final String API_PREFIX = "/api/v1"; - private static final String DEFAULT_SERVER = "localhost"; + public static final String DEFAULT_SERVER_NAME = "localhost"; private final CloseableHttpClient httpClient; @@ -85,30 +87,59 @@ public class PowerDnsClient implements AutoCloseable { .build(); } - public void validate(String baseUrl, String apiKey) throws DnsProviderException { - String url = buildUrl(baseUrl, "/servers"); + public String resolveServerId(String baseUrl, Integer port, String apiKey, String externalServerId) throws DnsProviderException { + if (StringUtils.isNotBlank(externalServerId)) { + return validateServerId(baseUrl, port, apiKey, externalServerId); + } + return discoverAuthoritativeServerId(baseUrl, port, apiKey); + } + + public String validateServerId(String baseUrl, Integer port, String apiKey, String externalServerId) throws DnsProviderException { + String encodedServer = URLEncoder.encode(externalServerId, StandardCharsets.UTF_8); + HttpGet request = new HttpGet(buildUrl(baseUrl, port, "/servers/" + encodedServer)); + JsonNode server = execute(request, apiKey, 200); + if (!ApiConstants.AUTHORITATIVE.equalsIgnoreCase(server.path("daemon_type").asText(null))) { + throw new DnsOperationException(String.format("Server %s is not authoritative type=%s", externalServerId, + server.path("daemon_type").asText(null))); + } + return externalServerId; + } + + public String discoverAuthoritativeServerId(String baseUrl, Integer port, String apiKey) throws DnsProviderException { + String url = buildUrl(baseUrl, port , "/servers"); HttpGet request = new HttpGet(url); JsonNode servers = execute(request, apiKey, 200); if (servers == null || !servers.isArray() || servers.isEmpty()) { throw new DnsOperationException("No servers returned by PowerDNS API"); } - boolean authoritativeFound = false; + String fallbackId = null; for (JsonNode server : servers) { - if (ApiConstants.AUTHORITATIVE.equalsIgnoreCase(server.path("daemon_type").asText(null))) { - authoritativeFound = true; - break; + String daemonType = server.path("daemon_type").asText(null); + if (!ApiConstants.AUTHORITATIVE.equalsIgnoreCase(daemonType)) { + continue; + } + String serverId = server.path(ApiConstants.ID).asText(null); + if (StringUtils.isBlank(serverId)) { + continue; + } + // Prefer localhost if present + if (DEFAULT_SERVER_NAME.equals(serverId)) { + return serverId; + } + if (fallbackId == null) { + fallbackId = serverId; } } - if (!authoritativeFound) { - throw new DnsOperationException("No authoritative PowerDNS server found"); + if (fallbackId != null) { + return fallbackId; } + throw new DnsOperationException("No authoritative PowerDNS server found"); } - public String createZone(String baseUrl, String apiKey, String zoneName, String zoneKind, boolean dnsSecFlag, - List nameServers) throws DnsProviderException { - - validate(baseUrl, apiKey); + public String createZone(String baseUrl, Integer port, String apiKey, String externalServerId, String zoneName, + String zoneKind, boolean dnsSecFlag, List nameServers) throws DnsProviderException { + validateServerId(baseUrl, port, externalServerId, apiKey); String normalizedZone = normalizeZone(zoneName); ObjectNode json = MAPPER.createObjectNode(); json.put(ApiConstants.NAME, normalizedZone); @@ -120,7 +151,7 @@ public class PowerDnsClient implements AutoCloseable { nsArray.add(ns.endsWith(".") ? ns : ns + "."); } } - HttpPost request = new HttpPost(buildUrl(baseUrl, "/servers/" + DEFAULT_SERVER + "/zones")); + HttpPost request = new HttpPost(buildUrl(baseUrl, port, "/servers/" + externalServerId + "/zones")); request.setEntity(new StringEntity(json.toString(), StandardCharsets.UTF_8)); JsonNode response = execute(request, apiKey, 201); if (response == null) { @@ -133,15 +164,15 @@ public class PowerDnsClient implements AutoCloseable { return zoneId; } - public void updateZone(String baseUrl, String apiKey, String zoneName, String zoneKind, Boolean dnsSecFlag, - List nameServers) throws DnsProviderException { + public void updateZone(String baseUrl, Integer port, String apiKey, String externalServerId, String zoneName, + String zoneKind, Boolean dnsSecFlag, List nameServers) throws DnsProviderException { + validateServerId(baseUrl, port, externalServerId, apiKey); String normalizedZone = normalizeZone(zoneName); String encodedZone = URLEncoder.encode(normalizedZone, StandardCharsets.UTF_8); - String url = buildUrl(baseUrl, "/servers/" + DEFAULT_SERVER + "/zones/" + encodedZone); + String url = buildUrl(baseUrl, port,"/servers/" + externalServerId + "/zones/" + encodedZone); ObjectNode json = MAPPER.createObjectNode(); - if (dnsSecFlag != null) { json.put(ApiConstants.DNS_SEC, dnsSecFlag); } @@ -159,18 +190,18 @@ public class PowerDnsClient implements AutoCloseable { execute(request, apiKey, 204); } - public void deleteZone(String baseUrl, String apiKey, String zoneName) throws DnsProviderException { - validate(baseUrl, apiKey); + public void deleteZone(String baseUrl, Integer port, String apiKey, String externalServerId, String zoneName) throws DnsProviderException { + validateServerId(baseUrl, port, apiKey, externalServerId); String normalizedZone = normalizeZone(zoneName); String encodedZone = URLEncoder.encode(normalizedZone, StandardCharsets.UTF_8); - HttpDelete request = new HttpDelete(buildUrl(baseUrl, "/servers/" + DEFAULT_SERVER + "/zones/" + encodedZone)); + HttpDelete request = new HttpDelete(buildUrl(baseUrl, port, "/servers/" + externalServerId + "/zones/" + encodedZone)); execute(request, apiKey, 204, 404); } - public String modifyRecord(String baseUrl, String apiKey, String zoneName, String recordName, String type, long ttl, - List contents, String changeType) throws DnsProviderException { + public String modifyRecord(String baseUrl, Integer port, String apiKey, String externalServerId, String zoneName, + String recordName, String type, long ttl, List contents, String changeType) throws DnsProviderException { - validate(baseUrl, apiKey); + validateServerId(baseUrl, port, apiKey, externalServerId); String normalizedZone = normalizeZone(zoneName); String normalizedRecord = normalizeRecordName(recordName, normalizedZone); ObjectNode root = MAPPER.createObjectNode(); @@ -189,17 +220,17 @@ public class PowerDnsClient implements AutoCloseable { } } String encodedZone = URLEncoder.encode(normalizedZone, StandardCharsets.UTF_8); - HttpPatch request = new HttpPatch(buildUrl(baseUrl, "/servers/" + DEFAULT_SERVER + "/zones/" + encodedZone)); + HttpPatch request = new HttpPatch(buildUrl(baseUrl, port, "/servers/" + externalServerId + "/zones/" + encodedZone)); request.setEntity(new org.apache.http.entity.StringEntity(root.toString(), StandardCharsets.UTF_8)); execute(request, apiKey, 204); return normalizedRecord; } - public Iterable listRecords(String baseUrl, String apiKey, String zoneName) throws DnsProviderException { - validate(baseUrl, apiKey); + public Iterable listRecords(String baseUrl, Integer port, String apiKey, String externalServerId, String zoneName) throws DnsProviderException { + validateServerId(baseUrl, port, apiKey, externalServerId); String normalizedZone = normalizeZone(zoneName); String encodedZone = URLEncoder.encode(normalizedZone, StandardCharsets.UTF_8); - HttpGet request = new HttpGet(buildUrl(baseUrl, "/servers/" + DEFAULT_SERVER + "/zones/" + encodedZone)); + HttpGet request = new HttpGet(buildUrl(baseUrl, port, "/servers/" + externalServerId + "/zones/" + encodedZone)); JsonNode zoneNode = execute(request, apiKey, 200); if (zoneNode == null || !zoneNode.has(ApiConstants.RR_SETS)) { return Collections.emptyList(); @@ -239,8 +270,22 @@ public class PowerDnsClient implements AutoCloseable { } } - private String buildUrl(String baseUrl, String path) { - return normalizeBaseUrl(baseUrl) + API_PREFIX + path; + private String buildUrl(String baseUrl, Integer port, String path) { + String fullUrl = normalizeBaseUrl(baseUrl); + if (port != null && port > 0) { + try { + URI uri = new URI(fullUrl); + if (uri.getPort() == -1) { + fullUrl = fullUrl + ":" + port; + } + } catch (URISyntaxException e) { + fullUrl = fullUrl + ":" + port; + } + } + if (!path.startsWith("/")) { + path = "/" + path; + } + return fullUrl + API_PREFIX + path; } private String normalizeBaseUrl(String baseUrl) { diff --git a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java index 9570a1e5f95..44862ff2e25 100644 --- a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java +++ b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.dns.DnsProvider; import org.apache.cloudstack.dns.DnsProviderType; import org.apache.cloudstack.dns.DnsRecord; @@ -42,32 +43,44 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { } public void validate(DnsServer server) throws DnsProviderException { - validateServerParams(server); - client.validate(server.getUrl(), server.getApiKey()); + validateRequiredServerFields(server); + client.validateServerId(server.getUrl(), server.getPort(), server.getApiKey(), server.getExternalServerId()); + } + + @Override + public String validateAndResolveServer(DnsServer server) throws Exception { + validateRequiredServerFields(server); + return client.resolveServerId(server.getUrl(), server.getPort(), server.getApiKey(), server.getExternalServerId()); } @Override public String provisionZone(DnsServer server, DnsZone zone) throws DnsProviderException { - validateServerZoneParams(server, zone); - return client.createZone(server.getUrl(), + validateRequiredServerAndZoneFields(server, zone); + return client.createZone( + server.getUrl(), + server.getPort(), server.getApiKey(), + server.getExternalServerId(), zone.getName(), - "Native", - false, - server.getNameServers() + ApiConstants.NATIVE_ZONE, false, server.getNameServers() ); } @Override public void deleteZone(DnsServer server, DnsZone zone) throws DnsProviderException { - validateServerZoneParams(server, zone); - client.deleteZone(server.getUrl(), server.getApiKey(), zone.getName()); + validateRequiredServerAndZoneFields(server, zone); + client.deleteZone(server.getUrl(), server.getPort(), server.getApiKey(), server.getExternalServerId(), zone.getName()); } @Override public void updateZone(DnsServer server, DnsZone zone) throws DnsProviderException { - validateServerZoneParams(server, zone); - client.updateZone(server.getUrl(), server.getApiKey(), zone.getName(), "Native", false, server.getNameServers()); + validateRequiredServerAndZoneFields(server, zone); + client.updateZone( + server.getUrl(), + server.getPort(), + server.getApiKey(), + server.getExternalServerId(), + zone.getName(), ApiConstants.NATIVE_ZONE, false, server.getNameServers()); } public enum ChangeType { @@ -76,42 +89,56 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { @Override public String addRecord(DnsServer server, DnsZone zone, DnsRecord record) throws DnsProviderException { - validateServerZoneParams(server, zone); - return applyRecord(server.getUrl(), server.getApiKey(), zone.getName(), record, ChangeType.REPLACE); + validateRequiredServerAndZoneFields(server, zone); + return applyRecord( + server.getUrl(), + server.getPort(), + server.getApiKey(), + server.getExternalServerId(), + zone.getName(), record, ChangeType.REPLACE); } @Override public String updateRecord(DnsServer server, DnsZone zone, DnsRecord record) throws DnsProviderException { - validateServerZoneParams(server, zone); + validateRequiredServerAndZoneFields(server, zone); return addRecord(server, zone, record); } @Override public void deleteRecord(DnsServer server, DnsZone zone, DnsRecord record) throws DnsProviderException { - validateServerZoneParams(server, zone); - applyRecord(server.getUrl(), server.getApiKey(), zone.getName(), record, ChangeType.DELETE); + validateRequiredServerAndZoneFields(server, zone); + applyRecord(server.getUrl(), + server.getPort(), + server.getApiKey(), + server.getExternalServerId(), + zone.getName(), record, ChangeType.DELETE); } - public String applyRecord(String serverUrl, String apiKey, String zoneName, DnsRecord record, ChangeType changeType) throws DnsProviderException { - return client.modifyRecord(serverUrl, apiKey, zoneName, record.getName(), record.getType().name(), - record.getTtl(), record.getContents(), changeType.name()); + public String applyRecord(String serverUrl, Integer port, String apiKey, String externalServerId, String zoneName, + DnsRecord record, ChangeType changeType) throws DnsProviderException { + + return client.modifyRecord(serverUrl, port, apiKey, externalServerId, zoneName, record.getName(), + record.getType().name(), record.getTtl(), record.getContents(), changeType.name()); } @Override public List listRecords(DnsServer server, DnsZone zone) throws DnsProviderException { - validateServerZoneParams(server, zone); + validateRequiredServerAndZoneFields(server, zone); List records = new ArrayList<>(); - for (JsonNode rrset: client.listRecords(server.getUrl(), server.getApiKey(), zone.getName())) { - String name = rrset.path("name").asText(); - String typeStr = rrset.path("type").asText(); - int ttl = rrset.path("ttl").asInt(0); + Iterable rrsetNodes = client.listRecords(server.getUrl(), server.getPort(), server.getApiKey(), + server.getExternalServerId(), zone.getName()); + + for (JsonNode rrset : rrsetNodes) { + String name = rrset.path(ApiConstants.NAME).asText(); + String typeStr = rrset.path(ApiConstants.TYPE).asText(); + int ttl = rrset.path(ApiConstants.TTL).asInt(0); if (!"SOA".equalsIgnoreCase(typeStr)) { try { List contents = new ArrayList<>(); - JsonNode recordsNode = rrset.path("records"); + JsonNode recordsNode = rrset.path(ApiConstants.RECORDS); if (recordsNode.isArray()) { for (JsonNode rec : recordsNode) { - String content = rec.path("content").asText(); + String content = rec.path(ApiConstants.CONTENT).asText(); if (!content.isEmpty()) { contents.add(content); } @@ -126,14 +153,14 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { return records; } - void validateServerZoneParams(DnsServer server, DnsZone zone) throws DnsProviderException { - validateServerParams(server); + void validateRequiredServerAndZoneFields(DnsServer server, DnsZone zone) { + validateRequiredServerFields(server); if (StringUtils.isBlank(zone.getName())) { throw new IllegalArgumentException("Zone name cannot be empty"); } } - void validateServerParams(DnsServer server) { + void validateRequiredServerFields(DnsServer server) { if (StringUtils.isBlank(server.getUrl())) { throw new IllegalArgumentException("PowerDNS API URL cannot be empty"); } diff --git a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java index 57e4cd14d4c..6739ccb0b11 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -109,17 +109,29 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa throw new InvalidParameterValueException( "This Account already has a DNS server integration for URL: " + cmd.getUrl()); } + + boolean isDnsPublic = cmd.isPublic(); + String publicDomainSuffix = cmd.getPublicDomainSuffix(); + if (caller.getType().equals(Account.Type.NORMAL)) { + logger.info("Only admin and domain admin users are allowed to configure a public DNS server"); + isDnsPublic = false; + publicDomainSuffix = null; + } DnsProviderType type = DnsProviderType.fromString(cmd.getProvider()); - DnsProvider provider = getProvider(type); - DnsServerVO server = new DnsServerVO(cmd.getName(), cmd.getUrl(), type, cmd.getCredentials(), cmd.getPort(), - cmd.isPublic(), cmd.getPublicDomainSuffix(), cmd.getNameServers(), caller.getId()); + DnsServerVO server = new DnsServerVO(cmd.getName(), cmd.getUrl(), cmd.getPort(), cmd.getExternalServerId(), type, + cmd.getDnsUserName(), cmd.getCredentials(), isDnsPublic, publicDomainSuffix, cmd.getNameServers(), + caller.getAccountId(), caller.getDomainId()); try { - provider.validate(server); + DnsProvider provider = getProvider(type); + String dnsServerId = provider.validateAndResolveServer(server); // localhost for PowerDNS + if (StringUtils.isNotBlank(dnsServerId)) { + server.setExternalServerId(dnsServerId); + } + return dnsServerDao.persist(server); } catch (Exception ex) { logger.error("Failed to validate DNS server", ex); throw new CloudRuntimeException("Failed to validate DNS server"); } - return dnsServerDao.persist(server); } @Override @@ -246,6 +258,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa response.setProvider(server.getProviderType()); response.setPublic(server.isPublic()); response.setNameServers(server.getNameServers()); + response.setPublicDomainSuffix(server.getPublicDomainSuffix()); response.setObjectName("dnsserver"); return response; } diff --git a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java index 45fb81d64f4..2b922f1063e 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java +++ b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java @@ -44,7 +44,7 @@ import com.cloud.utils.db.GenericDao; @Entity @Table(name = "dns_server") -public class DnsServerVO implements DnsServer { +public class DnsServerVO implements DnsServer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -66,10 +66,16 @@ public class DnsServerVO implements DnsServer { @Enumerated(EnumType.STRING) private DnsProviderType providerType; + @Column(name = "dns_user_name") + private String dnsUserName; + @Encrypt @Column(name = "api_key") private String apiKey; + @Column(name = "external_server_id") + private String externalServerId; + @Column(name = "is_public") private boolean isPublic; @@ -91,7 +97,7 @@ public class DnsServerVO implements DnsServer { @Column(name = GenericDao.CREATED_COLUMN) @Temporal(value = TemporalType.TIMESTAMP) - private Date created = null; + private Date created; @Column(name = GenericDao.REMOVED_COLUMN) @Temporal(value = TemporalType.TIMESTAMP) @@ -102,16 +108,18 @@ public class DnsServerVO implements DnsServer { this.created = new Date(); } - public DnsServerVO(String name, String url, DnsProviderType providerType, String apiKey, - Integer port, boolean isPublic, String publicDomainSuffix, List nameServers, - long accountId) { + public DnsServerVO(String name, String url, Integer port, String externalServerId, DnsProviderType providerType, String dnsUserName, String apiKey, + boolean isPublic, String publicDomainSuffix, List nameServers, Long accountId, Long domainId) { this(); this.name = name; this.url = url; this.port = port; + this.externalServerId = externalServerId; this.providerType = providerType; + this.dnsUserName = dnsUserName; this.apiKey = apiKey; this.accountId = accountId; + this.domainId = domainId; this.publicDomainSuffix = publicDomainSuffix; this.isPublic = isPublic; this.state = State.Enabled; @@ -229,4 +237,20 @@ public class DnsServerVO implements DnsServer { public long getDomainId() { return domainId; } + + public String getPublicDomainSuffix() { + return publicDomainSuffix; + } + + public String getExternalServerId() { + return externalServerId; + } + + public void setExternalServerId(String externalServerId) { + this.externalServerId = externalServerId; + } + + public Integer getPort() { + return this.port; + } }