DnsNodeRepositoryMock.java
/*
* Copyright 2019 the original author or authors.
*
* Licensed 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.
*/
package org.bremersee.dccon.repository;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.time.OffsetDateTime;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.bremersee.dccon.config.DomainControllerProperties;
import org.bremersee.dccon.model.DnsNode;
import org.bremersee.dccon.model.DnsRecord;
import org.bremersee.dccon.model.UnknownFilter;
import org.bremersee.exception.ServiceException;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Profile;
import org.springframework.context.event.EventListener;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The dns node repository mock.
*
* @author Christian Bremer
*/
@Profile("!ldap")
@Component
@Slf4j
public class DnsNodeRepositoryMock extends AbstractDnsNodeRepository implements MockRepository {
private static final String NODES_LOCATION = "classpath:demo/nodes.json";
private static final String REVERSE_ZONE = "1.168.192.in-addr.arpa";
private static final String DISTINGUISHED_NAME_TEMPLATE = "DC=%s,DC=%s,"
+ "CN=MicrosoftDNS,DC=DomainDnsZones,DC=samdom,DC=example,DC=org";
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
private final Map<String, Map<String, DnsNode>> repo = new ConcurrentHashMap<>();
private final ObjectMapper objectMapper;
/**
* Instantiates a new dns node repository mock.
*
* @param properties the properties
* @param dhcpRepository the dhcp repository
* @param dnsZoneRepository the dns zone repository
* @param objectMapperBuilder the object mapper builder
*/
public DnsNodeRepositoryMock(
DomainControllerProperties properties,
DhcpRepository dhcpRepository,
DnsZoneRepository dnsZoneRepository,
Jackson2ObjectMapperBuilder objectMapperBuilder) {
super(properties, null, dhcpRepository, dnsZoneRepository);
this.objectMapper = objectMapperBuilder.build();
}
/**
* Init.
*/
@EventListener(ApplicationReadyEvent.class)
public void init() {
log.warn("\n"
+ "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
+ "!! MOCK is running: DnsNodeRepository !!\n"
+ "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
);
resetData();
}
@Override
public void resetData() {
DnsNode[] dnsNodes;
try {
dnsNodes = objectMapper.readValue(
resourceLoader.getResource(NODES_LOCATION).getInputStream(),
DnsNode[].class);
} catch (IOException e) {
throw ServiceException.internalServerError("Loading demo data failed.", e);
}
Set<String> zones = new HashSet<>();
zones.add(getProperties().getDefaultZone());
zones.add(REVERSE_ZONE);
getDnsZoneRepository().findAll().forEach(dnsZone -> {
if (!zones.contains(dnsZone.getName())) {
getDnsZoneRepository().delete(dnsZone.getName());
}
});
zones.forEach(zone -> getDnsZoneRepository().save(zone));
for (DnsNode dnsNode : dnsNodes) {
save(getProperties().getDefaultZone(), dnsNode);
}
}
@Override
public Stream<DnsNode> findAll(String zoneName, UnknownFilter unknownFilter, String query) {
if (query == null || query.trim().length() == 0) {
return getNodeMap(zoneName).values().stream()
.filter(this::isNonExcludedDnsNode)
.map(dnsNode -> insertCorrelationValues(zoneName, dnsNode))
.map(dnsNode -> insertDhcpLeases(zoneName, dnsNode));
} else {
return getNodeMap(zoneName).values().stream()
.filter(this::isNonExcludedDnsNode)
.map(dnsNode -> build(dnsNode, zoneName))
.map(dnsNode -> insertCorrelationValues(zoneName, dnsNode))
.map(dnsNode -> insertDhcpLeases(zoneName, dnsNode))
.filter(dnsNode -> this.isQueryResult(dnsNode, query));
}
}
@Override
public boolean exists(String zoneName, String nodeName, UnknownFilter unknownFilter) {
return getNodeMap(zoneName).containsKey(nodeName.toLowerCase());
}
@Override
Optional<DnsNode> findOne(
final String zoneName,
final String nodeName,
final UnknownFilter unknownFilter,
final boolean withCorrelationValues,
final boolean withDhcpLeases) {
return Optional.ofNullable(build(getNodeMap(zoneName).get(nodeName.toLowerCase()), zoneName))
.filter(this::isNonExcludedDnsNode)
.map(dnsNode -> withCorrelationValues
? insertCorrelationValues(zoneName, dnsNode)
: dnsNode)
.map(dnsNode -> withDhcpLeases
? insertDhcpLeases(zoneName, dnsNode)
: dnsNode);
}
@Override
public Optional<DnsNode> save(String zoneName, DnsNode dnsNode) {
Assert.hasText(dnsNode.getName(), "Node name must be present.");
if (dnsNode.getRecords().isEmpty()) {
delete(zoneName, dnsNode.getName());
return Optional.empty();
}
final DnsNode newDnsNode = dnsNode.toBuilder()
.distinguishedName(null)
.records(dnsNode.getRecords().stream()
.filter(dnsRecord -> StringUtils.hasText(dnsRecord.getRecordValue())
&& StringUtils.hasText(dnsRecord.getRecordType()))
.map(dnsRecord -> dnsRecord.toBuilder()
.recordRawValue(null)
.correlatedRecordValue(null)
.dhcpLease(null)
.build())
.collect(Collectors.toSet()))
.build();
if (newDnsNode.getCreated() == null) {
newDnsNode.setCreated(OffsetDateTime.now());
}
if (newDnsNode.getModified() == null) {
newDnsNode.setModified(newDnsNode.getCreated());
}
final Set<DnsRecord> deletedRecords = new LinkedHashSet<>();
final Set<DnsRecord> newRecords = new LinkedHashSet<>();
final DnsNode oldDnsNode = getNodeMap(zoneName).get(newDnsNode.getName().toLowerCase());
if (oldDnsNode == null) {
newRecords.addAll(newDnsNode.getRecords());
} else {
for (DnsRecord dnsRecord : oldDnsNode.getRecords()) {
if (!newDnsNode.getRecords().contains(dnsRecord)) {
deletedRecords.add(dnsRecord);
}
}
for (DnsRecord dnsRecord : newDnsNode.getRecords()) {
if (!oldDnsNode.getRecords().contains(dnsRecord)) {
newRecords.add(dnsRecord);
}
}
}
getNodeMap(zoneName).put(newDnsNode.getName().toLowerCase(), newDnsNode);
handlePtrRecords(zoneName, newDnsNode.getName(), newRecords, deletedRecords);
return findOne(zoneName, newDnsNode.getName(), null, true, true);
}
@Override
public boolean delete(String zoneName, DnsNode node) {
boolean result = getNodeMap(zoneName).remove(node.getName().toLowerCase()) != null;
if (result) {
handlePtrRecords(zoneName, node.getName(), Collections.emptySet(), node.getRecords());
}
return result;
}
private Map<String, DnsNode> getNodeMap(String zoneName) {
return repo.computeIfAbsent(zoneName.toLowerCase(), key -> new ConcurrentHashMap<>());
}
private DnsNode build(DnsNode dnsNode, String zoneName) {
return dnsNode == null ? null : dnsNode.toBuilder()
.distinguishedName(String.format(DISTINGUISHED_NAME_TEMPLATE, dnsNode.getName(), zoneName))
.build();
}
}