mirror of https://github.com/apache/cloudstack.git
fix list dnsservers api, ui screens for dns servers, generate events
This commit is contained in:
parent
369bb16d0d
commit
0df50cedf8
|
|
@ -864,8 +864,10 @@ public class EventTypes {
|
|||
|
||||
// DNS Framework Events
|
||||
public static final String EVENT_DNS_SERVER_ADD = "DNS.SERVER.ADD";
|
||||
public static final String EVENT_DNS_SERVER_UPDATE = "DNS.SERVER.UPDATE";
|
||||
public static final String EVENT_DNS_SERVER_DELETE = "DNS.SERVER.DELETE";
|
||||
public static final String EVENT_DNS_ZONE_CREATE = "DNS.ZONE.CREATE";
|
||||
public static final String EVENT_DNS_ZONE_UPDATE = "DNS.ZONE.UPDATE";
|
||||
public static final String EVENT_DNS_ZONE_DELETE = "DNS.ZONE.DELETE";
|
||||
public static final String EVENT_DNS_RECORD_CREATE = "DNS.RECORD.CREATE";
|
||||
public static final String EVENT_DNS_RECORD_DELETE = "DNS.RECORD.DELETE";
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package com.cloud.user;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
|
|
@ -27,6 +26,9 @@ import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
|||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeyCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
|
|
@ -35,8 +37,7 @@ import com.cloud.network.vpc.VpcOffering;
|
|||
import com.cloud.offering.DiskOffering;
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import com.cloud.utils.Pair;
|
||||
|
||||
public interface AccountService {
|
||||
|
||||
|
|
@ -119,6 +120,8 @@ public interface AccountService {
|
|||
|
||||
void checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException;
|
||||
|
||||
void checkAccess(Account account, DnsServer dnsServer) throws PermissionDeniedException;
|
||||
|
||||
void checkAccess(User user, ControlledEntity entity);
|
||||
|
||||
void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException;
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ public class AddDnsServerCmd extends BaseCmd {
|
|||
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "API URL of the provider")
|
||||
private String url;
|
||||
|
||||
@Parameter(name = ApiConstants.PROVIDER_TYPE, type = CommandType.STRING, required = true, description = "Provider type (e.g., PowerDNS)")
|
||||
private String providerType;
|
||||
@Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, required = true, description = "Provider type (e.g., PowerDNS)")
|
||||
private String provider;
|
||||
|
||||
@Parameter(name = ApiConstants.DNS_USER_NAME, type = CommandType.STRING,
|
||||
description = "Username or email associated with the external DNS provider account (used for authentication)")
|
||||
|
|
@ -109,8 +109,8 @@ public class AddDnsServerCmd extends BaseCmd {
|
|||
return nameServers;
|
||||
}
|
||||
|
||||
public DnsProviderType getProviderType() {
|
||||
DnsProviderType dnsProviderType = EnumUtils.getEnumIgnoreCase(DnsProviderType.class, providerType, DnsProviderType.PowerDNS);
|
||||
public DnsProviderType getProvider() {
|
||||
DnsProviderType dnsProviderType = EnumUtils.getEnumIgnoreCase(DnsProviderType.class, provider, DnsProviderType.PowerDNS);
|
||||
if (dnsProviderType == null) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value passed for provider type, valid values are: %s",
|
||||
EnumUtils.listValues(DnsProviderType.values())));
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import org.apache.cloudstack.api.response.DnsZoneResponse;
|
|||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.component.Manager;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.vm.Nic;
|
||||
|
|
@ -75,7 +74,5 @@ public interface DnsProviderManager extends Manager, PluggableService {
|
|||
|
||||
boolean disassociateZoneFromNetwork(DisassociateDnsZoneFromNetworkCmd cmd);
|
||||
|
||||
void checkDnsServerPermissions(Account caller, DnsServer server);
|
||||
|
||||
String processDnsRecordForInstance(VirtualMachine instance, Network network, Nic nic, boolean isAdd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ import org.apache.cloudstack.affinity.AffinityGroup;
|
|||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.dns.DnsProviderManager;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -103,8 +101,6 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
|
|||
private ProjectDao projectDao;
|
||||
@Inject
|
||||
private AccountService accountService;
|
||||
@Inject
|
||||
private DnsProviderManager dnsProviderManager;
|
||||
|
||||
protected DomainChecker() {
|
||||
super();
|
||||
|
|
@ -220,8 +216,6 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
|
|||
_networkMgr.checkRouterPermissions(caller, (VirtualRouter)entity);
|
||||
} else if (entity instanceof AffinityGroup) {
|
||||
return false;
|
||||
} else if (entity instanceof DnsServer) {
|
||||
dnsProviderManager.checkDnsServerPermissions(caller, (DnsServer) entity);
|
||||
} else {
|
||||
validateCallerHasAccessToEntityOwner(caller, entity, accessType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,9 +179,9 @@ import com.cloud.user.dao.UserDataDao;
|
|||
import com.cloud.utils.ConstantTimeComparator;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.UuidUtils;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.Manager;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
|
|
@ -737,8 +737,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
}
|
||||
if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate)
|
||||
&& !(entity instanceof Network && (accessType == AccessType.UseEntry || accessType == AccessType.OperateEntry))
|
||||
&& !(entity instanceof AffinityGroup) && !(entity instanceof VirtualRouter)
|
||||
&& !(entity instanceof DnsServer)) {
|
||||
&& !(entity instanceof AffinityGroup) && !(entity instanceof VirtualRouter)) {
|
||||
List<ControlledEntity> toBeChecked = domains.get(entity.getDomainId());
|
||||
// for templates, we don't have to do cross domains check
|
||||
if (toBeChecked == null) {
|
||||
|
|
@ -3636,6 +3635,20 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
throw new PermissionDeniedException("There's no way to confirm " + account + " has access to " + bof);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Account caller, DnsServer dnsServer) throws PermissionDeniedException {
|
||||
if (caller.getId() == dnsServer.getAccountId()) {
|
||||
return;
|
||||
}
|
||||
if (!dnsServer.isPublicServer()) {
|
||||
throw new PermissionDeniedException(caller + "is not allowed to access the DNS server " + dnsServer.getName());
|
||||
}
|
||||
Account owner = getAccount(dnsServer.getAccountId());
|
||||
if (!_domainDao.isChildDomain(owner.getDomainId(), caller.getDomainId())) {
|
||||
throw new PermissionDeniedException(caller + "is not allowed to access the DNS server " + dnsServer.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(User user, ControlledEntity entity) throws PermissionDeniedException {
|
||||
for (SecurityChecker checker : _securityCheckers) {
|
||||
|
|
|
|||
|
|
@ -60,22 +60,20 @@ import org.apache.cloudstack.dns.vo.DnsZoneVO;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.event.ActionEvent;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.projects.Project;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
|
@ -119,6 +117,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server")
|
||||
public DnsServer addDnsServer(AddDnsServerCmd cmd) {
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
DnsServer existing = dnsServerDao.findByUrlAndAccount(cmd.getUrl(), caller.getId());
|
||||
|
|
@ -139,7 +138,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
publicDomainSuffix = DnsProviderUtil.normalizeDomain(publicDomainSuffix);
|
||||
}
|
||||
|
||||
DnsProviderType type = cmd.getProviderType();
|
||||
DnsProviderType type = cmd.getProvider();
|
||||
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());
|
||||
|
|
@ -159,12 +158,15 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
@Override
|
||||
public ListResponse<DnsServerResponse> listDnsServers(ListDnsServersCmd cmd) {
|
||||
Pair<List<DnsServerVO>, Integer> result = searchForDnsServerInternal(cmd);
|
||||
ListResponse<DnsServerResponse> response = new ListResponse<>();
|
||||
if (result == null) {
|
||||
return response;
|
||||
}
|
||||
List<String> serverIds = new ArrayList<>();
|
||||
for (DnsServer server : result.first()) {
|
||||
serverIds.add(server.getUuid());
|
||||
}
|
||||
List<DnsServerJoinVO> joinResult = dnsServerJoinDao.listByUuids(serverIds);
|
||||
ListResponse<DnsServerResponse> response = new ListResponse<>();
|
||||
List<DnsServerResponse> serverResponses = new ArrayList<>();
|
||||
for (DnsServerJoinVO server : joinResult) {
|
||||
serverResponses.add(createDnsServerResponse(server));
|
||||
|
|
@ -176,64 +178,20 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
private Pair<List<DnsServerVO>, Integer> searchForDnsServerInternal(ListDnsServersCmd cmd) {
|
||||
Long dnsServerId = cmd.getId();
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
Long domainId = cmd.getDomainId();
|
||||
boolean isRecursive = cmd.isRecursive();
|
||||
|
||||
// Step 1: Build ACL search parameters based on caller permissions
|
||||
List<Long> permittedAccountIds = new ArrayList<>();
|
||||
Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(
|
||||
domainId, isRecursive, null);
|
||||
accountMgr.buildACLSearchParameters(caller, dnsServerId, cmd.getAccountName(), null, permittedAccountIds,
|
||||
domainIdRecursiveListProject, cmd.listAll(), false);
|
||||
|
||||
domainId = domainIdRecursiveListProject.first();
|
||||
isRecursive = domainIdRecursiveListProject.second();
|
||||
Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
||||
Filter searchFilter = new Filter(DnsServerVO.class, ApiConstants.ID, true, cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
|
||||
// Step 2: Search for caller's own DNS servers using standard ACL pattern
|
||||
SearchBuilder<DnsServerVO> sb = dnsServerDao.createSearchBuilder();
|
||||
accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccountIds, listProjectResourcesCriteria);
|
||||
sb.and(ApiConstants.STATE, sb.entity().getState(), SearchCriteria.Op.EQ);
|
||||
sb.and(ApiConstants.PROVIDER_TYPE, sb.entity().getProviderType(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
|
||||
SearchCriteria<DnsServerVO> sc = sb.create();
|
||||
accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccountIds, listProjectResourcesCriteria);
|
||||
sc.setParameters(ApiConstants.STATE, DnsServer.State.Enabled);
|
||||
sc.setParameters(ApiConstants.PROVIDER_TYPE, cmd.getProviderType());
|
||||
|
||||
Pair<List<DnsServerVO>, Integer> ownServersPair = dnsServerDao.searchAndCount(sc, searchFilter);
|
||||
List<DnsServerVO> dnsServers = new ArrayList<>(ownServersPair.first());
|
||||
int count = ownServersPair.second();
|
||||
if (cmd.getId() == null) {
|
||||
Set<Long> parentDomainIds = domainDao.getDomainParentIds(caller.getDomainId());
|
||||
if (!parentDomainIds.isEmpty()) {
|
||||
SearchBuilder<DnsServerVO> publicSb = dnsServerDao.createSearchBuilder();
|
||||
publicSb.and(ApiConstants.IS_PUBLIC, publicSb.entity().isPublicServer(), SearchCriteria.Op.EQ);
|
||||
publicSb.and(ApiConstants.DOMAIN_IDS, publicSb.entity().getDomainId(), SearchCriteria.Op.IN);
|
||||
publicSb.and(ApiConstants.STATE, publicSb.entity().getState(), SearchCriteria.Op.EQ);
|
||||
publicSb.and(ApiConstants.PROVIDER_TYPE, publicSb.entity().getProviderType(), SearchCriteria.Op.EQ);
|
||||
publicSb.done();
|
||||
SearchCriteria<DnsServerVO> publicSc = publicSb.create();
|
||||
publicSc.setParameters(ApiConstants.IS_PUBLIC, 1);
|
||||
publicSc.setParameters(ApiConstants.DOMAIN_IDS, parentDomainIds.toArray());
|
||||
publicSc.setParameters(ApiConstants.STATE, DnsServer.State.Enabled);
|
||||
publicSc.setParameters(ApiConstants.PROVIDER_TYPE, cmd.getProviderType());
|
||||
List<DnsServerVO> publicServers = dnsServerDao.search(publicSc, null);
|
||||
List<Long> ownServerIds = dnsServers.stream().map(DnsServerVO::getId).collect(Collectors.toList());
|
||||
for (DnsServerVO publicServer : publicServers) {
|
||||
if (!ownServerIds.contains(publicServer.getId())) {
|
||||
dnsServers.add(publicServer);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (dnsServerId != null) {
|
||||
DnsServerVO dnsServerVO = dnsServerDao.findById(dnsServerId);
|
||||
if (dnsServerVO == null) {
|
||||
return null;
|
||||
}
|
||||
return new Pair<>(Collections.singletonList(dnsServerVO), 1);
|
||||
}
|
||||
return new Pair<>(dnsServers, count);
|
||||
Set<Long> parentDomainIds = domainDao.getDomainParentIds(caller.getDomainId());
|
||||
Filter searchFilter = new Filter(DnsServerVO.class, ApiConstants.ID, true, cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
return dnsServerDao.searchDnsServer(dnsServerId, caller.getAccountId(), parentDomainIds, cmd.getProviderType(), cmd.getKeyword(), searchFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_UPDATE, eventDescription = "Updating DNS Server")
|
||||
public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) {
|
||||
Long dnsServerId = cmd.getId();
|
||||
DnsServerVO dnsServer = dnsServerDao.findById(dnsServerId);
|
||||
|
|
@ -242,7 +200,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
accountMgr.checkAccess(caller, null, true, dnsServer);
|
||||
accountMgr.checkAccess(caller, dnsServer);
|
||||
|
||||
boolean validationRequired = false;
|
||||
String originalUrl = dnsServer.getUrl();
|
||||
|
|
@ -305,6 +263,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_DELETE, eventDescription = "Deleting DNS Server")
|
||||
public boolean deleteDnsServer(DeleteDnsServerCmd cmd) {
|
||||
Long dnsServerId = cmd.getId();
|
||||
DnsServerVO dnsServer = dnsServerDao.findById(dnsServerId);
|
||||
|
|
@ -312,11 +271,12 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
throw new InvalidParameterValueException(String.format("DNS server with ID: %s not found.", dnsServerId));
|
||||
}
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
accountMgr.checkAccess(caller, null, true, dnsServer);
|
||||
accountMgr.checkAccess(caller, dnsServer);
|
||||
return dnsServerDao.remove(dnsServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_ZONE_DELETE, eventDescription = "Deleting DNS Zone")
|
||||
public boolean deleteDnsZone(Long zoneId) {
|
||||
DnsZoneVO zone = dnsZoneDao.findById(zoneId);
|
||||
if (zone == null) {
|
||||
|
|
@ -340,6 +300,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_ZONE_UPDATE, eventDescription = "Updating DNS Zone")
|
||||
public DnsZone updateDnsZone(UpdateDnsZoneCmd cmd) {
|
||||
DnsZoneVO dnsZone = dnsZoneDao.findById(cmd.getId());
|
||||
if (dnsZone == null) {
|
||||
|
|
@ -396,7 +357,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
Account caller = CallContext.current().getCallingAccount();
|
||||
if (cmd.getDnsServerId() != null) {
|
||||
DnsServer dnsServer = dnsServerDao.findById(cmd.getDnsServerId());
|
||||
accountMgr.checkAccess(caller, null, false, dnsServer);
|
||||
accountMgr.checkAccess(caller, dnsServer);
|
||||
}
|
||||
List<Long> ownDnsServerIds = dnsServerDao.listDnsServerIdsByAccountId(caller.getAccountId());
|
||||
String keyword = cmd.getKeyword();
|
||||
|
|
@ -408,6 +369,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_RECORD_CREATE, eventDescription = "Creating DNS Record")
|
||||
public DnsRecordResponse createDnsRecord(CreateDnsRecordCmd cmd) {
|
||||
String recordName = StringUtils.trimToEmpty(cmd.getName()).toLowerCase();
|
||||
if (StringUtils.isBlank(recordName)) {
|
||||
|
|
@ -436,6 +398,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_RECORD_DELETE, eventDescription = "Deleting DNS Record")
|
||||
public boolean deleteDnsRecord(DeleteDnsRecordCmd cmd) {
|
||||
DnsZoneVO zone = dnsZoneDao.findById(cmd.getDnsZoneId());
|
||||
if (zone == null) {
|
||||
|
|
@ -651,21 +614,6 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
return dnsZoneNetworkMapDao.remove(mapping.getId());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void checkDnsServerPermissions(Account caller, DnsServer server) {
|
||||
if (caller.getId() == server.getAccountId()) {
|
||||
return;
|
||||
}
|
||||
if (!server.isPublicServer()) {
|
||||
throw new PermissionDeniedException(caller + "is not allowed to access the DNS server " + server.getName());
|
||||
}
|
||||
Account owner = accountMgr.getAccount(server.getAccountId());
|
||||
if (!domainDao.isChildDomain(caller.getDomainId(), owner.getDomainId())) {
|
||||
throw new PermissionDeniedException(caller + "is not allowed to access the DNS server " + server.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processDnsRecordForInstance(VirtualMachine instance, Network network, Nic nic, boolean isAdd) {
|
||||
long networkId = network.getId();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@
|
|||
package org.apache.cloudstack.dns.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cloudstack.dns.DnsProviderType;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
import org.apache.cloudstack.dns.vo.DnsServerVO;
|
||||
|
||||
|
|
@ -32,4 +34,6 @@ public interface DnsServerDao extends GenericDao<DnsServerVO, Long> {
|
|||
List<Long> listDnsServerIdsByAccountId(Long accountId);
|
||||
|
||||
Pair<List<DnsServerVO>, Integer> searchDnsServers(Long id, String keyword, String provider, Long accountId, Filter filter);
|
||||
|
||||
Pair<List<DnsServerVO>, Integer> searchDnsServer(Long dnsServerId, Long accountId, Set<Long> domainIds, DnsProviderType providerType, String keyword, Filter filter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@
|
|||
package org.apache.cloudstack.dns.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.dns.DnsProviderType;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
import org.apache.cloudstack.dns.vo.DnsServerVO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
|
|
@ -96,4 +99,44 @@ public class DnsServerDaoImpl extends GenericDaoBase<DnsServerVO, Long> implemen
|
|||
}
|
||||
return searchAndCount(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<DnsServerVO>, Integer> searchDnsServer(Long dnsServerId, Long accountId, Set<Long> domainIds, DnsProviderType providerType,
|
||||
String keyword, Filter filter) {
|
||||
|
||||
SearchBuilder<DnsServerVO> sb = createSearchBuilder();
|
||||
sb.and(ApiConstants.ID, sb.entity().getId(), SearchCriteria.Op.EQ);
|
||||
sb.and(ApiConstants.NAME, sb.entity().getName(), SearchCriteria.Op.LIKE);
|
||||
|
||||
sb.and().op(ApiConstants.ACCOUNT_ID, sb.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||
if (!CollectionUtils.isEmpty(domainIds)) {
|
||||
sb.or().op(ApiConstants.IS_PUBLIC, sb.entity().isPublicServer(), SearchCriteria.Op.EQ);
|
||||
sb.and(ApiConstants.DOMAIN_IDS, sb.entity().getDomainId(), SearchCriteria.Op.IN);
|
||||
sb.cp();
|
||||
}
|
||||
sb.cp();
|
||||
sb.and(ApiConstants.PROVIDER_TYPE, sb.entity().getProviderType(), SearchCriteria.Op.EQ);
|
||||
sb.and(ApiConstants.STATE, sb.entity().getState(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
|
||||
SearchCriteria<DnsServerVO> sc = sb.create();
|
||||
if (dnsServerId != null) {
|
||||
sc.setParameters(ApiConstants.ID, dnsServerId);
|
||||
}
|
||||
if (accountId != null) {
|
||||
sc.setParameters(ApiConstants.ACCOUNT_ID, accountId);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(domainIds)) {
|
||||
sc.setParameters(ApiConstants.IS_PUBLIC, true);
|
||||
sc.setParameters(ApiConstants.DOMAIN_IDS, domainIds.toArray());
|
||||
}
|
||||
if (providerType != null) {
|
||||
sc.setParameters(ApiConstants.PROVIDER_TYPE, providerType);
|
||||
}
|
||||
if (keyword != null) {
|
||||
sc.setParameters(ApiConstants.NAME, "%" + keyword + "%");
|
||||
}
|
||||
sc.setParameters(ApiConstants.STATE, DnsServer.State.Enabled);
|
||||
return searchAndCount(sc, filter);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@
|
|||
"label.action.delete.account": "Delete Account",
|
||||
"label.action.delete.backup.offering": "Delete backup offering",
|
||||
"label.action.delete.cluster": "Delete Cluster",
|
||||
"label.action.delete.dns.server": "Delete DNS Server",
|
||||
"label.action.delete.domain": "Delete Domain",
|
||||
"label.action.delete.egress.firewall": "Delete Egress Firewall Rule",
|
||||
"label.action.delete.firewall": "Delete Firewall Rule",
|
||||
|
|
@ -292,6 +293,10 @@
|
|||
"label.add.ip.range": "Add IP Range",
|
||||
"label.add.ipv4.subnet": "Add IPv4 Subnet for Routed Networks",
|
||||
"label.dns.server": "DNS Server",
|
||||
"label.dns.add.server": "Add DNS Server",
|
||||
"label.dns.update.server": "Update DNS Server",
|
||||
"label.dns.delete.server": "Delete DNS Server",
|
||||
"label.dnsrecords": "DNS Records",
|
||||
"label.add.ip.v6.prefix": "Add IPv6 prefix",
|
||||
"label.add.isolated.network": "Add Isolated Network",
|
||||
"label.add.kubernetes.cluster": "Add Kubernetes Cluster",
|
||||
|
|
@ -713,6 +718,8 @@
|
|||
"label.created": "Created",
|
||||
"label.creating": "Creating",
|
||||
"label.creating.iprange": "Creating IP ranges",
|
||||
"label.dns.credentials": "DNS API key",
|
||||
"label.nameservers": "DNS Nameservers",
|
||||
"label.credit": "Credit",
|
||||
"label.cron": "Cron expression",
|
||||
"label.cron.mode": "Cron mode",
|
||||
|
|
@ -2947,6 +2954,7 @@
|
|||
"message.action.delete.backup.schedule": "Please confirm that you want to delete this backup schedule?",
|
||||
"message.action.delete.cluster": "Please confirm that you want to delete this Cluster.",
|
||||
"message.action.delete.custom.action": "Please confirm that you want to delete this custom action.",
|
||||
"message.action.delete.dns.server": "Please confirm you want to delete this DNS server.",
|
||||
"message.action.delete.domain": "Please confirm that you want to delete this domain.",
|
||||
"message.action.delete.extension": "Please confirm that you want to delete the extension",
|
||||
"message.action.delete.external.firewall": "Please confirm that you would like to remove this external firewall. Warning: If you are planning to add back the same external firewall, you must reset usage data on the device.",
|
||||
|
|
|
|||
|
|
@ -207,6 +207,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<a-list-item v-else-if="item === 'provider' && $route.path.includes('/dnsserver')">
|
||||
<div>
|
||||
<strong>{{ $t('label.provider') }}</strong>
|
||||
<br/>
|
||||
<div>{{ dataResource[item] }}</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<external-configuration-details
|
||||
v-else-if="item === 'externaldetails' && (['host', 'computeoffering'].includes($route.meta.name) || (['cluster'].includes($route.meta.name) && dataResource.extensionid))"
|
||||
:resource="dataResource" />
|
||||
|
|
|
|||
|
|
@ -1510,24 +1510,60 @@ export default {
|
|||
permission: ['listDnsZones'],
|
||||
columns: ['name', 'state', 'dnsservername', 'dnsserveraccount'],
|
||||
details: ['name', 'state', 'dnsservername', 'dnsserveraccount'],
|
||||
related: [{
|
||||
tabs: [{
|
||||
name: 'details',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
|
||||
},
|
||||
{
|
||||
name: 'dnsrecords',
|
||||
title: 'label.dns.records',
|
||||
param: 'dnszoneid'
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/network/InternalLBAssignedVmTab.vue'))),
|
||||
show: () => true
|
||||
}]
|
||||
},
|
||||
{
|
||||
name: 'dnsservers',
|
||||
name: 'dnsserver',
|
||||
title: 'label.dns.server',
|
||||
icon: 'global-outlined',
|
||||
permission: ['listDnsServers'],
|
||||
columns: ['name', 'url', 'provider'],
|
||||
details: ['name', 'url', 'ispublic', 'port', 'nameservers', 'domain', 'account'],
|
||||
details: ['name', 'url', 'abc', 'provider', 'ispublic', 'port', 'nameservers', 'domain', 'account'],
|
||||
related: [{
|
||||
name: 'dnszones',
|
||||
title: 'label.dns.zone',
|
||||
param: 'dnsserverid'
|
||||
}]
|
||||
}],
|
||||
actions: [
|
||||
{
|
||||
api: 'addDnsServer',
|
||||
icon: 'plus-outlined',
|
||||
label: 'label.dns.add.server',
|
||||
listView: true,
|
||||
popup: true,
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/AddDnsServer.vue'))),
|
||||
show: () => {
|
||||
return true
|
||||
}
|
||||
},
|
||||
{
|
||||
api: 'updateDnsServer',
|
||||
icon: 'edit-outlined',
|
||||
label: 'label.dns.update.server',
|
||||
dataView: true,
|
||||
popup: true,
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/AddDnsServer.vue'))),
|
||||
show: (record) => { return true }
|
||||
},
|
||||
{
|
||||
api: 'deleteDnsServer',
|
||||
icon: 'delete-outlined',
|
||||
label: 'label.dns.delete.server',
|
||||
message: 'message.action.delete.dns.server',
|
||||
dataView: true,
|
||||
groupAction: true,
|
||||
show: (record) => { return true },
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
layout="vertical">
|
||||
|
||||
<a-form-item name="name" ref="name">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.name')"
|
||||
:tooltip="apiParams.name?.description" />
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.name"
|
||||
:placeholder="apiParams.name?.description"
|
||||
v-focus="true" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="url" ref="url">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.url')"
|
||||
:tooltip="apiParams.url?.description" />
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.url"
|
||||
:placeholder="apiParams.url?.description" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="provider" ref="provider">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.provider')"
|
||||
:tooltip="apiParams.provider?.description" />
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="form.provider"
|
||||
:placeholder="apiParams.provider?.description || 'Select Provider'"
|
||||
:loading="fetchingProviders"
|
||||
showSearch>
|
||||
<a-select-option
|
||||
v-for="provider in providers"
|
||||
:key="provider.name || provider"
|
||||
:value="provider.name || provider">
|
||||
{{ provider.description || provider.name || provider }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="port" ref="port">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.port')"
|
||||
:tooltip="apiParams.port?.description" />
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model:value="form.port"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
style="width: 100%" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="credentials" ref="credentials">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.dns.credentials')"
|
||||
:tooltip="apiParams.credentials?.description" />
|
||||
</template>
|
||||
<a-input-password
|
||||
v-model:value="form.credentials"
|
||||
:placeholder="apiParams.credentials?.description || 'Enter API Key'" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="nameservers" ref="nameservers">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.nameservers')"
|
||||
:tooltip="apiParams.nameservers?.description" />
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.nameservers"
|
||||
:placeholder="apiParams.nameservers?.description" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="ispublic" ref="ispublic">
|
||||
<a-checkbox v-model:checked="form.ispublic">
|
||||
{{ "Public server" }}
|
||||
</a-checkbox>
|
||||
</a-form-item>
|
||||
|
||||
<div class="action-button">
|
||||
<a-button @click="closeAction">
|
||||
{{ $t('label.cancel') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="handleSubmit">
|
||||
{{ $t('label.ok') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAPI, postAPI } from '@/api'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
|
||||
export default {
|
||||
name: 'AddDnsServer',
|
||||
components: {
|
||||
TooltipLabel
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
apiParams: {},
|
||||
form: {
|
||||
name: '',
|
||||
url: '',
|
||||
provider: '',
|
||||
port: 8081,
|
||||
nameservers: '',
|
||||
ispublic: false
|
||||
},
|
||||
rules: {},
|
||||
fetchingProviders: false,
|
||||
providers: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.apiParams = this.$getApiParams('addDnsServer') || {}
|
||||
this.rules = {
|
||||
name: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
url: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
provider: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
credentials: [{ required: true, message: this.$t('message.error.required.input') }]
|
||||
}
|
||||
this.fetchProviders()
|
||||
},
|
||||
methods: {
|
||||
async handleSubmit () {
|
||||
if (this.loading) return
|
||||
|
||||
// 1. Validate the form natively using Vue 3 $refs
|
||||
try {
|
||||
await this.$refs.formRef.validate()
|
||||
} catch (error) {
|
||||
// Scroll to the first field with an error
|
||||
if (error.errorFields && error.errorFields.length > 0) {
|
||||
this.$refs.formRef.scrollToField(error.errorFields[0].name)
|
||||
}
|
||||
return // Abort submission
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
|
||||
// 2. Execute the API
|
||||
try {
|
||||
console.log('form data ', this.form)
|
||||
// Pass the form object directly
|
||||
await postAPI('addDnsServer', this.form)
|
||||
|
||||
this.$notification.success({
|
||||
message: this.$t('label.add.dns.server'),
|
||||
description: this.$t('message.success.add.dns.server')
|
||||
})
|
||||
|
||||
// Refresh the parent table and close modal
|
||||
this.$emit('refresh-data')
|
||||
this.closeAction()
|
||||
} catch (error) {
|
||||
this.$notification.error({
|
||||
message: this.$t('message.request.failed'),
|
||||
description: error?.response?.headers['x-description'] || error.message,
|
||||
duration: 0
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
},
|
||||
async fetchProviders () {
|
||||
this.fetchingProviders = true
|
||||
try {
|
||||
const response = await getAPI('listDnsProviders')
|
||||
const listResponse = response?.listdnsprovidersresponse || {}
|
||||
this.providers = listResponse.dnsprovider || listResponse.provider || []
|
||||
if (this.providers.length > 0) {
|
||||
const defaultProvider = this.providers[0]
|
||||
this.form.provider = defaultProvider.name || defaultProvider
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch DNS providers', error)
|
||||
this.$message.warning('Could not load DNS providers.')
|
||||
} finally {
|
||||
this.fetchingProviders = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.form-layout {
|
||||
width: 80vw;
|
||||
@media (min-width: 600px) {
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
text-align: right;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.action-button button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue