1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.dccon.repository;
18
19 import static org.bremersee.dccon.repository.cli.CommandExecutorResponse.toExceptionMessage;
20
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Optional;
24 import java.util.regex.Pattern;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
27 import lombok.extern.slf4j.Slf4j;
28 import org.bremersee.data.ldaptive.LdaptiveEntryMapper;
29 import org.bremersee.data.ldaptive.LdaptiveTemplate;
30 import org.bremersee.dccon.config.DomainControllerProperties;
31 import org.bremersee.dccon.model.DnsZone;
32 import org.bremersee.dccon.repository.cli.CommandExecutor;
33 import org.bremersee.dccon.repository.cli.CommandExecutorResponse;
34 import org.bremersee.dccon.repository.cli.CommandExecutorResponseParser;
35 import org.bremersee.dccon.repository.cli.CommandExecutorResponseValidator;
36 import org.bremersee.dccon.repository.ldap.DnsZoneLdapMapper;
37 import org.bremersee.exception.ServiceException;
38 import org.ldaptive.SearchFilter;
39 import org.ldaptive.SearchRequest;
40 import org.springframework.beans.factory.ObjectProvider;
41 import org.springframework.context.annotation.Profile;
42 import org.springframework.stereotype.Component;
43
44
45
46
47
48
49 @Profile("ldap")
50 @Component("dnsZoneRepository")
51 @Slf4j
52 public class DnsZoneRepositoryImpl extends AbstractRepository implements DnsZoneRepository {
53
54 private final List<Pattern> excludedZoneNamePatterns;
55
56 private LdaptiveEntryMapper<DnsZone> dnsZoneLdapMapper;
57
58
59
60
61
62
63
64 public DnsZoneRepositoryImpl(
65 final DomainControllerProperties properties,
66 final ObjectProvider<LdaptiveTemplate> ldapTemplateProvider) {
67 super(properties, ldapTemplateProvider.getIfAvailable());
68 this.dnsZoneLdapMapper = new DnsZoneLdapMapper(properties);
69 this.excludedZoneNamePatterns = properties.getExcludedZoneRegexList().stream()
70 .map(Pattern::compile).collect(Collectors.toList());
71 }
72
73
74
75
76
77
78 @SuppressWarnings("unused")
79 public void setDnsZoneLdapMapper(final LdaptiveEntryMapper<DnsZone> dnsZoneLdapMapper) {
80 if (dnsZoneLdapMapper != null) {
81 this.dnsZoneLdapMapper = dnsZoneLdapMapper;
82 }
83 }
84
85 private boolean isNonExcludedDnsZone(final DnsZone zone) {
86 return zone != null && !isExcludedDnsZone(zone);
87 }
88
89 private boolean isNonExcludedDnsZone(final String zoneName) {
90 return zoneName != null && !isExcludedDnsZone(zoneName);
91 }
92
93 private boolean isExcludedDnsZone(final DnsZone zone) {
94 return zone != null && isExcludedDnsZone(zone.getName());
95 }
96
97 private boolean isExcludedDnsZone(final String zoneName) {
98 return zoneName != null && excludedZoneNamePatterns.stream()
99 .anyMatch(pattern -> pattern.matcher(zoneName).matches());
100 }
101
102 @Override
103 public boolean isDnsReverseZone(final String dnsZoneName) {
104 return getProperties().isReverseZone(dnsZoneName);
105 }
106
107 @Override
108 public Stream<DnsZone> findAll() {
109 final SearchRequest searchRequest = new SearchRequest(
110 getProperties().getDnsZoneBaseDn(),
111 new SearchFilter(getProperties().getDnsZoneFindAllFilter()));
112 searchRequest.setSearchScope(getProperties().getDnsZoneFindAllSearchScope());
113 return getLdapTemplate().findAll(searchRequest, dnsZoneLdapMapper)
114 .filter(this::isNonExcludedDnsZone);
115 }
116
117 @Override
118 public boolean exists(final String zoneName) {
119 return isNonExcludedDnsZone(zoneName)
120 && getLdapTemplate().exists(DnsZone.builder().name(zoneName).build(), dnsZoneLdapMapper);
121 }
122
123 @Override
124 public Optional<DnsZone> findOne(final String zoneName) {
125 final SearchFilter searchFilter = new SearchFilter(getProperties().getDnsZoneFindOneFilter());
126 searchFilter.setParameter(0, zoneName);
127 final SearchRequest searchRequest = new SearchRequest(
128 getProperties().getDnsZoneBaseDn(),
129 searchFilter);
130 searchRequest.setSearchScope(getProperties().getDnsZoneFindOneSearchScope());
131 return getLdapTemplate()
132 .findOne(searchRequest, dnsZoneLdapMapper)
133 .filter(this::isNonExcludedDnsZone);
134 }
135
136 @Override
137 public DnsZone save(final String zoneName) {
138 if (isExcludedDnsZone(zoneName)) {
139 throw ServiceException.badRequest(
140 "Zone name is not allowed.",
141 "org.bremersee:dc-con-app:bc02abb3-f5d9-4a95-9761-98def37d12a9");
142 }
143 return findOne(zoneName)
144 .orElseGet(() -> doSave(zoneName));
145 }
146
147
148
149
150
151
152
153 DnsZone doSave(final String zoneName) {
154 return execDnsZoneCmd(
155 "zonecreate",
156 zoneName, response -> findOne(zoneName)
157 .orElseThrow(() -> ServiceException.internalServerError(
158 "msg=[Saving dns zone failed.] "
159 + CommandExecutorResponse.toExceptionMessage(response),
160 "org.bremersee:dc-con-app:905a21c0-0ab9-4562-a83f-b849dbbea6c0")));
161 }
162
163 @Override
164 public boolean delete(final String zoneName) {
165 if (exists(zoneName)) {
166 doDelete(zoneName);
167 return true;
168 }
169 return false;
170 }
171
172
173
174
175
176
177 void doDelete(final String zoneName) {
178 execDnsZoneCmd(
179 "zonedelete",
180 zoneName,
181 (CommandExecutorResponseValidator) response -> {
182 if (exists(zoneName)) {
183 throw ServiceException.internalServerError(
184 "msg=[Deleting dns zone failed.] " + toExceptionMessage(response),
185 "org.bremersee:dc-con-app:346a54dd-c882-4c41-8503-7089928aeaa3");
186 }
187 });
188 }
189
190 private <T> T execDnsZoneCmd(
191 final String dnsCommand,
192 final String zoneName,
193 final CommandExecutorResponseParser<T> parser) {
194
195 kinit();
196 final List<String> commands = new ArrayList<>();
197 sudo(commands);
198 commands.add(getProperties().getSambaToolBinary());
199 commands.add("dns");
200 commands.add(dnsCommand);
201 commands.add(getProperties().getNameServerHost());
202 commands.add(zoneName);
203 auth(commands);
204 return CommandExecutor.exec(
205 commands, null, getProperties().getSambaToolExecDir(), parser);
206 }
207
208
209 }