mirror of https://github.com/apache/cloudstack.git
work on updateDnsServer ui screen, fixed acl issue for dns server and zone and cleanup methods for dns server and zone
This commit is contained in:
parent
d8cca73780
commit
a1eca74de1
|
|
@ -46,7 +46,6 @@ import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
|||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
|
||||
public interface AccountService {
|
||||
|
||||
|
|
@ -129,8 +128,6 @@ 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;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public class DeleteDnsServerCmd extends BaseAsyncCmd {
|
|||
|
||||
@Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN,
|
||||
entityType = DnsZoneResponse.class, description = "True if all associated DNS zones have to be cleaned up with this server")
|
||||
private Boolean cleanup;
|
||||
private Boolean cleanup = true;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ public class DnsServerResponse extends BaseResponse {
|
|||
@Param(description = "the name of the domain associated with the DNS server")
|
||||
private String domainName;
|
||||
|
||||
@SerializedName(ApiConstants.STATE)
|
||||
@Param(description = "The state of the account")
|
||||
private String state;
|
||||
|
||||
public DnsServerResponse() {
|
||||
super();
|
||||
|
||||
|
|
@ -122,4 +126,8 @@ public class DnsServerResponse extends BaseResponse {
|
|||
public void setDomainName(String domainName) {
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ 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,4 +76,8 @@ public interface DnsProviderManager extends Manager, PluggableService {
|
|||
boolean disassociateZoneFromNetwork(DisassociateDnsZoneFromNetworkCmd cmd);
|
||||
|
||||
String processDnsRecordForInstance(VirtualMachine instance, Network network, Nic nic, boolean isAdd);
|
||||
|
||||
void checkDnsServerPermission(Account caller, DnsServer dnsServer);
|
||||
|
||||
void checkDnsZonePermission(Account caller, DnsZone dnsZone);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,16 @@ import org.apache.cloudstack.acl.ProjectRoleService;
|
|||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
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.dns.DnsZone;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DedicatedResourceVO;
|
||||
import com.cloud.dc.dao.DedicatedResourceDao;
|
||||
|
|
@ -101,6 +104,8 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
|
|||
private ProjectDao projectDao;
|
||||
@Inject
|
||||
private AccountService accountService;
|
||||
@Inject
|
||||
private DnsProviderManager dnsProviderManager;
|
||||
|
||||
protected DomainChecker() {
|
||||
super();
|
||||
|
|
@ -216,7 +221,11 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
|
|||
_networkMgr.checkRouterPermissions(caller, (VirtualRouter)entity);
|
||||
} else if (entity instanceof AffinityGroup) {
|
||||
return false;
|
||||
} else {
|
||||
} else if (entity instanceof DnsServer) {
|
||||
dnsProviderManager.checkDnsServerPermission(caller, (DnsServer) entity);
|
||||
} else if (entity instanceof DnsZone) {
|
||||
dnsProviderManager.checkDnsZonePermission(caller, (DnsZone) entity);
|
||||
} else {
|
||||
validateCallerHasAccessToEntityOwner(caller, entity, accessType);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import org.apache.cloudstack.api.InternalIdentity;
|
|||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
|
@ -85,6 +86,8 @@ public class ActionEventUtils {
|
|||
EntityManager entityMgr;
|
||||
@Inject
|
||||
ConfigurationDao configDao;
|
||||
@Inject
|
||||
MessageBus messageBus;
|
||||
|
||||
public ActionEventUtils() {
|
||||
}
|
||||
|
|
@ -97,6 +100,7 @@ public class ActionEventUtils {
|
|||
s_projectDao = projectDao;
|
||||
s_entityMgr = entityMgr;
|
||||
s_configDao = configDao;
|
||||
messageBus = messageBus;
|
||||
}
|
||||
|
||||
public static Long onActionEvent(Long userId, Long accountId, Long domainId, String type, String description, Long resourceId, String resourceType) {
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ import org.apache.cloudstack.backup.BackupOffering;
|
|||
import org.apache.cloudstack.config.ApiServiceConfiguration;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
import org.apache.cloudstack.dns.DnsZone;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
|
|
@ -774,7 +775,8 @@ 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 AffinityGroup) && !(entity instanceof VirtualRouter)
|
||||
&& !(entity instanceof DnsServer) && !(entity instanceof DnsZone)) {
|
||||
List<ControlledEntity> toBeChecked = domains.get(entity.getDomainId());
|
||||
// for templates, we don't have to do cross domains check
|
||||
if (toBeChecked == null) {
|
||||
|
|
@ -3972,20 +3974,6 @@ 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.getPublicServer()) {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -71,11 +71,14 @@ import com.cloud.network.dao.NetworkDao;
|
|||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
|
@ -105,6 +108,8 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
DnsZoneJoinDao dnsZoneJoinDao;
|
||||
@Inject
|
||||
DnsServerJoinDao dnsServerJoinDao;
|
||||
@Inject
|
||||
AccountDao accountDao;
|
||||
|
||||
private DnsProvider getProviderByType(DnsProviderType type) {
|
||||
if (type == null) {
|
||||
|
|
@ -194,7 +199,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
accountMgr.checkAccess(caller, dnsServer);
|
||||
accountMgr.checkAccess(caller, null, true, dnsServer);
|
||||
|
||||
boolean validationRequired = false;
|
||||
String originalUrl = dnsServer.getUrl();
|
||||
|
|
@ -265,35 +270,55 @@ 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, dnsServer);
|
||||
if (cmd.getCleanup()) {
|
||||
// ToDo cleanup associated dnsZones
|
||||
}
|
||||
return dnsServerDao.remove(dnsServerId);
|
||||
accountMgr.checkAccess(caller, null, true, dnsServer);
|
||||
return Transaction.execute((TransactionCallback<Boolean>) status -> {
|
||||
if (cmd.getCleanup()) {
|
||||
List<DnsZoneVO> dnsZones = dnsZoneDao.findDnsZonesByServerId(dnsServerId);
|
||||
for (DnsZoneVO dnsZone : dnsZones) {
|
||||
long dnsZoneId = dnsZone.getId();
|
||||
dnsZoneNetworkMapDao.removeNetworkMappingByZoneId(dnsZoneId);
|
||||
// ToDo: delete nic_record_urls from vm_details if present before removing dnsZone
|
||||
dnsZoneDao.remove(dnsZoneId);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
DnsZoneVO dnsZone = dnsZoneDao.findById(zoneId);
|
||||
if (dnsZone == null) {
|
||||
throw new InvalidParameterValueException("DNS zone not found for the given ID.");
|
||||
}
|
||||
|
||||
String dnsZoneName = dnsZone.getName();
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
accountMgr.checkAccess(caller, null, true, zone);
|
||||
DnsServerVO server = dnsServerDao.findById(zone.getDnsServerId());
|
||||
if (server != null && zone.getState() == DnsZone.State.Active) {
|
||||
try {
|
||||
DnsProvider provider = getProviderByType(server.getProviderType());
|
||||
provider.deleteZone(server, zone);
|
||||
logger.debug("Deleted DNS zone: {}", zone.getName());
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to delete DNS zone from provider", ex);
|
||||
throw new CloudRuntimeException("Failed to delete DNS zone.");
|
||||
}
|
||||
accountMgr.checkAccess(caller, null, true, dnsZone);
|
||||
DnsServerVO server = dnsServerDao.findById(dnsZone.getDnsServerId());
|
||||
if (server == null) {
|
||||
throw new CloudRuntimeException(String.format("The DNS server not found for DNS zone: %s", dnsZoneName));
|
||||
}
|
||||
return dnsZoneDao.remove(zoneId);
|
||||
try {
|
||||
DnsProvider provider = getProviderByType(server.getProviderType());
|
||||
provider.deleteZone(server, dnsZone);
|
||||
logger.debug("Deleted DNS zone: {} from provider", dnsZoneName);
|
||||
} catch (DnsNotFoundException ex) {
|
||||
logger.warn("DNS zone: {} is not present in the provider, proceeding with cleanup", dnsZoneName);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to delete DNS zone from provider", ex);
|
||||
throw new CloudRuntimeException(String.format("Failed to delete DNS zone: %s.", dnsZoneName));
|
||||
}
|
||||
|
||||
boolean dbResult = Transaction.execute((TransactionCallback<Boolean>) status -> {
|
||||
dnsZoneNetworkMapDao.removeNetworkMappingByZoneId(zoneId);
|
||||
// ToDo: delete nic_record_urls from vm_details if present before removing dnsZone
|
||||
return dnsZoneDao.remove(zoneId);
|
||||
});
|
||||
if (!dbResult) {
|
||||
logger.error("Failed to remove DNS zone {} from DB after provider deletion", dnsZoneName);
|
||||
}
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -355,7 +380,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, dnsServer);
|
||||
accountMgr.checkAccess(caller, null, true, dnsServer);
|
||||
}
|
||||
List<Long> ownDnsServerIds = dnsServerDao.listDnsServerIdsByAccountId(caller.getAccountId());
|
||||
String keyword = cmd.getKeyword();
|
||||
|
|
@ -538,6 +563,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
response.setAccountName(server.getAccountName());
|
||||
response.setDomainId(server.getDomainUuid()); // Note: APIs always return UUIDs, not internal DB IDs!
|
||||
response.setDomainName(server.getDomainName());
|
||||
response.setState(server.getState().name());
|
||||
response.setObjectName("dnsserver");
|
||||
return response;
|
||||
}
|
||||
|
|
@ -677,6 +703,31 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkDnsServerPermission(Account caller, DnsServer dnsServer) throws PermissionDeniedException {
|
||||
if (caller.getId() == dnsServer.getAccountId()) {
|
||||
return;
|
||||
}
|
||||
if (!dnsServer.getPublicServer()) {
|
||||
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 checkDnsZonePermission(Account caller, DnsZone zone) {
|
||||
if (caller.getId() != zone.getAccountId()) {
|
||||
throw new PermissionDeniedException(caller + "is not allowed to access the DNS Zone " + zone.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public Account getAccount(long accountId) {
|
||||
return accountDao.findByIdIncludingRemoved(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
if (dnsProviders == null || dnsProviders.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ public class DnsVmLifecycleListener extends ManagerBase implements EventSubscrib
|
|||
eventBus.subscribe(new EventTopic(null, EventTypes.EVENT_VM_DESTROY, null, null, null), this);
|
||||
eventBus.subscribe(new EventTopic(null, EventTypes.EVENT_NIC_CREATE, null, null, null), this);
|
||||
eventBus.subscribe(new EventTopic(null, EventTypes.EVENT_NIC_DELETE, null, null, null), this);
|
||||
eventBus.subscribe(new EventTopic(null, EventTypes.EVENT_DNS_RECORD_DELETE, null, null, null), this);
|
||||
} catch (EventBusException ex) {
|
||||
logger.error("Failed to subscribe DnsVmLifecycleListener to EventBus", ex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,6 @@ public interface DnsZoneDao extends GenericDao<DnsZoneVO, Long> {
|
|||
|
||||
Pair<List<DnsZoneVO>, Integer> searchZones(Long id, Long accountId, List<Long> ownDnsServerIds, Long targetDnsServerId,
|
||||
String keyword, Filter filter);
|
||||
|
||||
List<DnsZoneVO> findDnsZonesByServerId(long dnsServerId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,12 +33,18 @@ import com.cloud.utils.db.SearchCriteria;
|
|||
|
||||
@Component
|
||||
public class DnsZoneDaoImpl extends GenericDaoBase<DnsZoneVO, Long> implements DnsZoneDao {
|
||||
SearchBuilder<DnsZoneVO> DnsServerSearch;
|
||||
SearchBuilder<DnsZoneVO> AccountSearch;
|
||||
SearchBuilder<DnsZoneVO> NameServerTypeSearch;
|
||||
|
||||
public DnsZoneDaoImpl() {
|
||||
super();
|
||||
|
||||
DnsServerSearch = createSearchBuilder();
|
||||
DnsServerSearch.selectFields(DnsServerSearch.entity().getDnsServerId());
|
||||
DnsServerSearch.and(ApiConstants.DNS_SERVER_ID, DnsServerSearch.entity().getDnsServerId(), SearchCriteria.Op.EQ);
|
||||
DnsServerSearch.done();
|
||||
|
||||
AccountSearch = createSearchBuilder();
|
||||
AccountSearch.and(ApiConstants.ACCOUNT_ID, AccountSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||
AccountSearch.and(ApiConstants.STATE, AccountSearch.entity().getState(), SearchCriteria.Op.EQ);
|
||||
|
|
@ -107,4 +113,10 @@ public class DnsZoneDaoImpl extends GenericDaoBase<DnsZoneVO, Long> implements D
|
|||
sc.setParameters(ApiConstants.STATE, DnsZone.State.Active);
|
||||
return searchAndCount(sc, filter);
|
||||
}
|
||||
|
||||
public List<DnsZoneVO> findDnsZonesByServerId(long dnsServerId) {
|
||||
SearchCriteria<DnsZoneVO> sc = DnsServerSearch.create();
|
||||
sc.setParameters(ApiConstants.DNS_SERVER_ID, dnsServerId);
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,6 @@ import org.apache.cloudstack.dns.vo.DnsZoneNetworkMapVO;
|
|||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface DnsZoneNetworkMapDao extends GenericDao<DnsZoneNetworkMapVO, Long> {
|
||||
DnsZoneNetworkMapVO findByZoneAndNetwork(long dnsZoneId, long networkId);
|
||||
void removeNetworkMappingByZoneId(long dnsZoneId);
|
||||
DnsZoneNetworkMapVO findByNetworkId(long networkId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ public class DnsZoneNetworkMapDaoImpl extends GenericDaoBase<DnsZoneNetworkMapVO
|
|||
super();
|
||||
ZoneNetworkSearch = createSearchBuilder();
|
||||
ZoneNetworkSearch.and(ApiConstants.DNS_ZONE_ID, ZoneNetworkSearch.entity().getDnsZoneId(), SearchCriteria.Op.EQ);
|
||||
ZoneNetworkSearch.and(ApiConstants.NETWORK_ID, ZoneNetworkSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
|
||||
ZoneNetworkSearch.done();
|
||||
|
||||
NetworkSearch = createSearchBuilder();
|
||||
|
|
@ -43,12 +42,10 @@ public class DnsZoneNetworkMapDaoImpl extends GenericDaoBase<DnsZoneNetworkMapVO
|
|||
}
|
||||
|
||||
@Override
|
||||
public DnsZoneNetworkMapVO findByZoneAndNetwork(long dnsZoneId, long networkId) {
|
||||
public void removeNetworkMappingByZoneId(long dnsZoneId) {
|
||||
SearchCriteria<DnsZoneNetworkMapVO> sc = ZoneNetworkSearch.create();
|
||||
sc.setParameters(ApiConstants.DNS_ZONE_ID, dnsZoneId);
|
||||
sc.setParameters(ApiConstants.NETWORK_ID, networkId);
|
||||
|
||||
return findOneBy(sc);
|
||||
remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -726,6 +726,7 @@
|
|||
"label.creating.dns.zone": "Creating DNS zone",
|
||||
"label.creating.iprange": "Creating IP ranges",
|
||||
"label.nameservers": "DNS Nameservers",
|
||||
"label.publicdomainsuffix": "Public domain suffix",
|
||||
"label.credit": "Credit",
|
||||
"label.cron": "Cron expression",
|
||||
"label.cron.mode": "Cron mode",
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@
|
|||
<template v-if="column.key === 'templatetype'">
|
||||
<span>{{ text }}</span>
|
||||
</template>
|
||||
<template v-if="$route.path.startsWith('/dnsserver') && !['name', 'provider'].includes(column.key)">
|
||||
<span>{{ text }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'gpu'">
|
||||
<span v-if="record.gpucardname && record.vgpuprofilename">
|
||||
{{ record?.gpucount > 0 ? record.gpucount + 'x' : '' }}
|
||||
|
|
|
|||
|
|
@ -1497,8 +1497,8 @@ export default {
|
|||
title: 'label.dns.server',
|
||||
icon: 'global-outlined',
|
||||
permission: ['listDnsServers'],
|
||||
columns: ['name', 'url', 'provider'],
|
||||
details: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'domain', 'account'],
|
||||
columns: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'publicdomainsuffix'],
|
||||
details: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'publicdomainsuffix', 'domain', 'account'],
|
||||
related: [{
|
||||
name: 'dnszone',
|
||||
title: 'label.dns.zone',
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
:placeholder="apiParams.externalserverid?.description || 'Enter Server ID of PowerDNS e.g. localhost'" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="publicdomainsuffix" ref="publicdomainsuffix">
|
||||
<a-form-item v-if="isAdminOrDomainAdmin()" name="publicdomainsuffix" ref="publicdomainsuffix">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.dns.publicdomainsuffix')"
|
||||
|
|
@ -128,10 +128,11 @@
|
|||
:placeholder="apiParams.nameservers?.description" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="ispublic" ref="ispublic">
|
||||
<a-checkbox v-model:checked="form.ispublic">
|
||||
{{ $t('label.ispublic') }}
|
||||
</a-checkbox>
|
||||
<a-form-item v-if="isAdminOrDomainAdmin()" name="ispublic" ref="ispublic">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.ispublic')" :tooltip="apiParams.ispublic?.description" />
|
||||
</template>
|
||||
<a-switch v-model:checked="form.ispublic" />
|
||||
</a-form-item>
|
||||
|
||||
<div class="action-button">
|
||||
|
|
@ -325,6 +326,9 @@ export default {
|
|||
}
|
||||
|
||||
return Promise.resolve()
|
||||
},
|
||||
isAdminOrDomainAdmin () {
|
||||
return ['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,10 @@ export default {
|
|||
name: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
type: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
contents: [{ required: true, type: 'array', min: 1, message: this.$t('message.error.required.input') }],
|
||||
ttl: [{ required: true, message: this.$t('message.error.required.input') }]
|
||||
ttl: [
|
||||
{ required: true, message: this.$t('message.error.required.input') },
|
||||
{ type: 'number', validator: this.validateNumber }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -191,6 +194,12 @@ export default {
|
|||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
},
|
||||
async validateNumber (rule, value) {
|
||||
if (value && (isNaN(value) || value <= 0)) {
|
||||
return Promise.reject(this.$t('message.error.number'))
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,42 @@
|
|||
style="width: 100%" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="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" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="isAdminOrDomainAdmin()" name="publicdomainsuffix">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.dns.publicdomainsuffix')" :tooltip="apiParams.publicdomainsuffix?.description" />
|
||||
</template>
|
||||
<a-input v-model:value="form.publicdomainsuffix" :placeholder="apiParams.publicdomainsuffix?.description" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="nameservers" ref="nameservers">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.nameservers')"
|
||||
:tooltip="apiParams.nameservers?.description" />
|
||||
</template>
|
||||
|
||||
<a-select
|
||||
v-model:value="form.nameservers"
|
||||
mode="tags"
|
||||
style="width: 100%"
|
||||
:token-separators="[',', ' ']"
|
||||
:placeholder="apiParams.nameservers?.description" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="isAdminOrDomainAdmin()" name="ispublic">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.ispublic')" :tooltip="apiParams.ispublic?.description" />
|
||||
</template>
|
||||
<a-switch v-model:checked="form.ispublic" />
|
||||
</a-form-item>
|
||||
|
||||
<div class="action-button">
|
||||
<a-button @click="closeAction">
|
||||
{{ $t('label.cancel') }}
|
||||
|
|
@ -107,11 +143,23 @@ export default {
|
|||
this.apiParams = this.$getApiParams('updateDnsServer') || {}
|
||||
this.rules = {
|
||||
name: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
url: [{ required: true, message: this.$t('message.error.required.input') }]
|
||||
url: [
|
||||
{ required: true, message: this.$t('message.error.required.input') },
|
||||
{ validator: this.validateUrl }
|
||||
],
|
||||
port: [
|
||||
{ required: true, message: this.$t('message.error.required.input') },
|
||||
{ validator: this.validatePort }
|
||||
],
|
||||
publicdomainsuffix: [{ validator: this.validatePublicDomainSuffix }],
|
||||
state: [{ required: true, message: this.$t('message.error.required.input') }]
|
||||
}
|
||||
this.form.name = this.resource.name || ''
|
||||
this.form.url = this.resource.url || ''
|
||||
this.form.port = this.resource.port || 53
|
||||
this.form.name = this.resource.name
|
||||
this.form.url = this.resource.url
|
||||
this.form.port = this.resource.port
|
||||
this.form.publicdomainsuffix = this.resource.publicdomainsuffix
|
||||
this.form.nameservers = this.resource.nameservers || []
|
||||
this.form.ispublic = this.resource.ispublic
|
||||
},
|
||||
methods: {
|
||||
async handleSubmit () {
|
||||
|
|
@ -129,8 +177,22 @@ export default {
|
|||
this.loading = true
|
||||
|
||||
try {
|
||||
await postAPI('updateDnsServer', { id: this.resource.id, ...this.form })
|
||||
|
||||
const params = {
|
||||
id: this.resource.id,
|
||||
name: this.form.name.trim(),
|
||||
url: this.form.url?.trim().replace(/\/$/, ''),
|
||||
port: this.form.port,
|
||||
nameservers: this.form.nameservers?.map(ns => ns.toLowerCase().trim()).filter(Boolean),
|
||||
ispublic: this.form.ispublic,
|
||||
state: this.form.state
|
||||
}
|
||||
if (this.form.credentials) {
|
||||
params.credentials = this.form.credentials
|
||||
}
|
||||
if (this.form.ispublic) {
|
||||
params.publicdomainsuffix = this.form.publicdomainsuffix?.trim().toLowerCase()
|
||||
}
|
||||
await postAPI('updateDnsServer', params)
|
||||
this.$notification.success({
|
||||
message: this.$t('label.dns.update.server'),
|
||||
description: this.$t('message.success.update.dns.server')
|
||||
|
|
@ -150,6 +212,71 @@ export default {
|
|||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
},
|
||||
validateUrl (rule, value) {
|
||||
if (!value) return Promise.resolve()
|
||||
try {
|
||||
const parsed = new URL(value)
|
||||
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||
return Promise.reject(new Error('URL must start with http:// or https://'))
|
||||
}
|
||||
if (parsed.port) {
|
||||
return Promise.reject(new Error('Do not include port in URL. Use the port field instead.'))
|
||||
}
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('Invalid URL format'))
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
validatePort (rule, value) {
|
||||
if (value === undefined || value === null) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
if (value < 1 || value > 65535) {
|
||||
return Promise.reject(new Error('Port must be between 1 and 65535'))
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
},
|
||||
validateNameservers (rule, value) {
|
||||
if (!value || !value.length) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const fqdnRegex = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
|
||||
|
||||
for (const ns of value) {
|
||||
if (!fqdnRegex.test(ns)) {
|
||||
return Promise.reject(new Error('Invalid nameserver'))
|
||||
}
|
||||
}
|
||||
|
||||
if (new Set(value).size !== value.length) {
|
||||
return Promise.reject(new Error('Nameservers must be unique'))
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
},
|
||||
validatePublicDomainSuffix (rule, value) {
|
||||
if (!this.form.ispublic) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
return Promise.reject(new Error(this.$t('message.error.required.publicdomainsuffix')))
|
||||
}
|
||||
|
||||
const fqdnRegex = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
|
||||
|
||||
if (!fqdnRegex.test(value)) {
|
||||
return Promise.reject(new Error('Invalid domain suffix'))
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
},
|
||||
isAdminOrDomainAdmin () {
|
||||
return ['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue