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.data.ldaptive;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.LinkedHashSet;
23  import java.util.List;
24  import java.util.Set;
25  import java.util.stream.Collectors;
26  import javax.validation.constraints.NotNull;
27  import org.ldaptive.AttributeModification;
28  import org.ldaptive.AttributeModification.Type;
29  import org.ldaptive.LdapAttribute;
30  import org.ldaptive.LdapEntry;
31  import org.ldaptive.ModifyRequest;
32  import org.ldaptive.beans.LdapEntryMapper;
33  import org.ldaptive.transcode.ValueTranscoder;
34  import org.springframework.lang.Nullable;
35  import org.springframework.validation.annotation.Validated;
36  
37  /**
38   * The ldap entry mapper.
39   *
40   * @param <T> the type of the domain object
41   * @author Christian Bremer
42   */
43  @Validated
44  public interface LdaptiveEntryMapper<T> extends LdapEntryMapper<T> {
45  
46    /**
47     * Get object classes of the ldap entry. The object classes are only required, if a new ldap entry should be
48     * persisted.
49     *
50     * @return the object classes of the ldap entry
51     */
52    String[] getObjectClasses();
53  
54    @Override
55    String mapDn(T domainObject);
56  
57    /**
58     * Map a ldap entry into a domain object.
59     *
60     * @param ldapEntry the ldap entry
61     * @return the domain object
62     */
63    @Nullable
64    T map(@Nullable LdapEntry ldapEntry);
65  
66    @Override
67    void map(LdapEntry source, T destination);
68  
69    @Override
70    default void map(T source, LdapEntry destination) {
71      mapAndComputeModifications(source, destination);
72    }
73  
74    /**
75     * Map and compute attribute modifications (see {@link LdapEntry#computeModifications(LdapEntry, LdapEntry)}**).
76     *
77     * @param source the source (domain object)
78     * @param destination the destination (ldap entry)
79     * @return the attribute modifications
80     */
81    AttributeModification[] mapAndComputeModifications(
82        @NotNull T source,
83        @NotNull LdapEntry destination);
84  
85    /**
86     * Map and compute modify request.
87     *
88     * @param source the source (domain object)
89     * @param destination the destination (ldap entry)
90     * @return the modify request
91     */
92    default ModifyRequest mapAndComputeModifyRequest(
93        @NotNull T source,
94        @NotNull LdapEntry destination) {
95      return new ModifyRequest(destination.getDn(), mapAndComputeModifications(source, destination));
96    }
97  
98    /**
99     * Gets attribute value.
100    *
101    * @param <T> the type parameter
102    * @param ldapEntry the ldap entry
103    * @param name the name
104    * @param valueTranscoder the value transcoder
105    * @param defaultValue the default value
106    * @return the attribute value
107    */
108   static <T> T getAttributeValue(
109       @Nullable final LdapEntry ldapEntry,
110       @NotNull final String name,
111       final ValueTranscoder<T> valueTranscoder,
112       final T defaultValue) {
113     final LdapAttribute attr = ldapEntry == null ? null : ldapEntry.getAttribute(name);
114     final T value = attr != null ? attr.getValue(valueTranscoder.decoder()) : null;
115     return value != null ? value : defaultValue;
116   }
117 
118   /**
119    * Gets attribute values.
120    *
121    * @param <T> the type parameter
122    * @param ldapEntry the ldap entry
123    * @param name the name
124    * @param valueTranscoder the value transcoder
125    * @return the attribute values
126    */
127   static <T> Collection<T> getAttributeValues(
128       @Nullable final LdapEntry ldapEntry,
129       @NotNull final String name,
130       final ValueTranscoder<T> valueTranscoder) {
131     final LdapAttribute attr = ldapEntry == null ? null : ldapEntry.getAttribute(name);
132     final Collection<T> values = attr != null ? attr.getValues(valueTranscoder.decoder()) : null;
133     return values != null ? values : new ArrayList<>();
134   }
135 
136   /**
137    * Gets attribute values as set.
138    *
139    * @param <T> the type parameter
140    * @param ldapEntry the ldap entry
141    * @param name the name
142    * @param valueTranscoder the value transcoder
143    * @return the attribute values as set
144    */
145   static <T> Set<T> getAttributeValuesAsSet(
146       @Nullable final LdapEntry ldapEntry,
147       @NotNull final String name,
148       final ValueTranscoder<T> valueTranscoder) {
149     return new LinkedHashSet<>(getAttributeValues(ldapEntry, name, valueTranscoder));
150   }
151 
152   /**
153    * Gets attribute values as list.
154    *
155    * @param <T> the type parameter
156    * @param ldapEntry the ldap entry
157    * @param name the name
158    * @param valueTranscoder the value transcoder
159    * @return the attribute values as list
160    */
161   static <T> List<T> getAttributeValuesAsList(
162       @Nullable final LdapEntry ldapEntry,
163       @NotNull final String name,
164       final ValueTranscoder<T> valueTranscoder) {
165     return new ArrayList<>(getAttributeValues(ldapEntry, name, valueTranscoder));
166   }
167 
168   /**
169    * Replaces the value of the attribute with the specified value.
170    *
171    * @param <T> the type of the domain object
172    * @param ldapEntry the ldap entry
173    * @param name the attribute name
174    * @param value the attribute value
175    * @param isBinary specifies whether the attribute value is binary or not
176    * @param valueTranscoder the value transcoder (can be null if value is also null)
177    * @param modifications the list of modifications
178    */
179   static <T> void setAttribute(
180       @NotNull final LdapEntry ldapEntry,
181       @NotNull final String name,
182       @Nullable final T value,
183       final boolean isBinary,
184       final ValueTranscoder<T> valueTranscoder,
185       @NotNull final List<AttributeModification> modifications) {
186 
187     setAttributes(
188         ldapEntry,
189         name,
190         value != null ? Collections.singleton(value) : null,
191         isBinary,
192         valueTranscoder,
193         modifications);
194   }
195 
196   /**
197    * Replaces the values of the attribute with the specified values.
198    *
199    * @param <T> the type of the domain object
200    * @param ldapEntry the ldap entry
201    * @param name the attribute name
202    * @param values the values of the attribute
203    * @param isBinary specifies whether the attribute value is binary or not
204    * @param valueTranscoder the value transcoder (can be null if values is also null)
205    * @param modifications the list of modifications
206    */
207   static <T> void setAttributes(
208       @NotNull final LdapEntry ldapEntry,
209       @NotNull final String name,
210       @Nullable final Collection<T> values,
211       final boolean isBinary,
212       final ValueTranscoder<T> valueTranscoder,
213       @NotNull final List<AttributeModification> modifications) {
214 
215     final Collection<T> realValues = values == null ? null : values.stream()
216         .filter(value -> {
217           if (value instanceof CharSequence) {
218             return ((CharSequence) value).length() > 0;
219           }
220           return value != null;
221         })
222         .collect(Collectors.toList());
223     LdapAttribute attr = ldapEntry.getAttribute(name);
224     if (attr == null && realValues != null && !realValues.isEmpty()) {
225       addAttributes(ldapEntry, name, realValues, isBinary, valueTranscoder, modifications);
226     } else if (attr != null) {
227       if (realValues == null || realValues.isEmpty()) {
228         ldapEntry.removeAttribute(name);
229         modifications.add(
230             new AttributeModification(
231                 Type.DELETE,
232                 attr));
233       } else if (!new ArrayList<>(realValues)
234           .equals(new ArrayList<>(attr.getValues(valueTranscoder.decoder())))) {
235         final LdapAttribute newAttr = new LdapAttribute();
236         newAttr.setBinary(isBinary);
237         newAttr.setName(name);
238         newAttr.addValues(valueTranscoder.encoder(), realValues);
239         ldapEntry.addAttributes(newAttr);
240         modifications.add(
241             new AttributeModification(
242                 Type.REPLACE,
243                 newAttr));
244       }
245     }
246   }
247 
248   /**
249    * Adds the specified value to the attribute with the specified name.
250    *
251    * @param <T> the type of the domain object
252    * @param ldapEntry the ldap entry
253    * @param name the attribute name
254    * @param value the attribute value
255    * @param isBinary specifies whether the attribute value is binary or not
256    * @param valueTranscoder the value transcoder
257    * @param modifications the list of modifications
258    */
259   static <T> void addAttribute(
260       @NotNull final LdapEntry ldapEntry,
261       @NotNull final String name,
262       @Nullable final T value,
263       final boolean isBinary,
264       @NotNull final ValueTranscoder<T> valueTranscoder,
265       @NotNull final List<AttributeModification> modifications) {
266     addAttributes(
267         ldapEntry,
268         name,
269         value != null ? Collections.singleton(value) : null,
270         isBinary,
271         valueTranscoder,
272         modifications);
273   }
274 
275   /**
276    * Adds the specified values to the attribute with the specified name.
277    *
278    * @param <T> the type of the domain object
279    * @param ldapEntry the ldap entry
280    * @param name the attribute name
281    * @param values the attribute values
282    * @param isBinary specifies whether the attribute value is binary or not
283    * @param valueTranscoder the value transcoder
284    * @param modifications the list of modifications
285    */
286   static <T> void addAttributes(
287       @NotNull final LdapEntry ldapEntry,
288       @NotNull final String name,
289       @Nullable final Collection<T> values,
290       final boolean isBinary,
291       @NotNull final ValueTranscoder<T> valueTranscoder,
292       @NotNull final List<AttributeModification> modifications) {
293     final Collection<T> realValues = values == null ? null : values.stream()
294         .filter(value -> {
295           if (value instanceof CharSequence) {
296             return ((CharSequence) value).length() > 0;
297           }
298           return value != null;
299         })
300         .collect(Collectors.toList());
301     if (realValues == null || realValues.isEmpty()) {
302       return;
303     }
304     final LdapAttribute attr = ldapEntry.getAttribute(name);
305     if (attr == null) {
306       final LdapAttribute newAttr = new LdapAttribute();
307       newAttr.setBinary(isBinary);
308       newAttr.setName(name);
309       newAttr.addValues(valueTranscoder.encoder(), realValues);
310       ldapEntry.addAttributes(newAttr);
311       modifications.add(
312           new AttributeModification(
313               Type.ADD,
314               newAttr));
315     } else {
316       final List<T> newValues = new ArrayList<>(
317           getAttributeValues(ldapEntry, name, valueTranscoder));
318       newValues.addAll(realValues);
319       setAttributes(ldapEntry, name, newValues, attr.isBinary(), valueTranscoder, modifications);
320     }
321   }
322 
323   /**
324    * Removes an attribute with the specified name.
325    *
326    * @param ldapEntry the ldap entry
327    * @param name the name
328    * @param modifications the modifications
329    */
330   static void removeAttribute(
331       @NotNull final LdapEntry ldapEntry,
332       @NotNull final String name,
333       @NotNull final List<AttributeModification> modifications) {
334     final LdapAttribute attr = ldapEntry.getAttribute(name);
335     if (attr == null) {
336       return;
337     }
338     ldapEntry.removeAttributes(attr);
339     modifications.add(
340         new AttributeModification(
341             Type.DELETE,
342             attr));
343   }
344 
345   /**
346    * Removes an attribute with the specified value. If the value is {@code null}, the whole attribute will be removed.
347    *
348    * @param <T> the type of the domain object
349    * @param ldapEntry the ldap entry
350    * @param name the name
351    * @param value the value
352    * @param valueTranscoder the value transcoder
353    * @param modifications the modifications
354    */
355   static <T> void removeAttribute(
356       @NotNull final LdapEntry ldapEntry,
357       @NotNull final String name,
358       @Nullable final T value,
359       final ValueTranscoder<T> valueTranscoder,
360       @NotNull final List<AttributeModification> modifications) {
361     LdapAttribute attr = ldapEntry.getAttribute(name);
362     if (attr == null) {
363       return;
364     }
365     if (value == null) {
366       removeAttribute(ldapEntry, name, modifications);
367     } else {
368       removeAttributes(ldapEntry, name, Collections.singleton(value), valueTranscoder,
369           modifications);
370     }
371   }
372 
373   /**
374    * Remove attributes with the specified values. If values are empty or {@code null}, no attributes will be removed.
375    *
376    * @param <T> the type of the domain object
377    * @param ldapEntry the ldap entry
378    * @param name the name
379    * @param values the values
380    * @param valueTranscoder the value transcoder
381    * @param modifications the modifications
382    */
383   static <T> void removeAttributes(
384       @NotNull final LdapEntry ldapEntry,
385       @NotNull final String name,
386       @Nullable final Collection<T> values,
387       final ValueTranscoder<T> valueTranscoder,
388       @NotNull final List<AttributeModification> modifications) {
389 
390     final LdapAttribute attr = ldapEntry.getAttribute(name);
391     if (attr == null || values == null || values.isEmpty()) {
392       return;
393     }
394     final List<T> newValues = new ArrayList<>(getAttributeValues(ldapEntry, name, valueTranscoder));
395     newValues.removeAll(values);
396     setAttributes(ldapEntry, name, newValues, attr.isBinary(), valueTranscoder, modifications);
397   }
398 
399   /**
400    * Create dn string.
401    *
402    * @param rdn the rdn
403    * @param rdnValue the rdn value
404    * @param baseDn the base dn
405    * @return the string
406    */
407   static String createDn(
408       @NotNull final String rdn,
409       @NotNull final String rdnValue,
410       @NotNull final String baseDn) {
411     return rdn + "=" + rdnValue + "," + baseDn;
412   }
413 
414   /**
415    * Gets rdn.
416    *
417    * @param dn the dn
418    * @return the rdn
419    */
420   static String getRdn(final String dn) {
421     if (dn == null) {
422       return null;
423     }
424     int start = dn.indexOf('=');
425     if (start < 0) {
426       return dn;
427     }
428     int end = dn.indexOf(',', start);
429     if (end < 0) {
430       return dn.substring(start + 1).trim();
431     }
432     return dn.substring(start + 1, end).trim();
433   }
434 
435 }