View Javadoc
1   /*
2    * Copyright 2019 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * The dns zone repository.
46   *
47   * @author Christian Bremer
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     * Instantiates a new dns zone repository.
60     *
61     * @param properties the properties
62     * @param ldapTemplateProvider the ldap template provider
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     * Sets dns zone ldap mapper.
75     *
76     * @param dnsZoneLdapMapper the dns zone ldap mapper
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    * Save dns zone.
149    *
150    * @param zoneName the zone name
151    * @return the dns zone
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    * Delete dns zone.
174    *
175    * @param zoneName the zone name
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 }