001/*
002 * Copyright 2016-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2016-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2016-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.experimental;
037
038
039
040import java.util.Collections;
041import java.util.List;
042
043import com.unboundid.ldap.sdk.DereferencePolicy;
044import com.unboundid.ldap.sdk.Entry;
045import com.unboundid.ldap.sdk.Filter;
046import com.unboundid.ldap.sdk.LDAPException;
047import com.unboundid.ldap.sdk.OperationType;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.ldap.sdk.SearchRequest;
050import com.unboundid.ldap.sdk.SearchScope;
051import com.unboundid.util.Debug;
052import com.unboundid.util.NotMutable;
053import com.unboundid.util.NotNull;
054import com.unboundid.util.Nullable;
055import com.unboundid.util.StaticUtils;
056import com.unboundid.util.ThreadSafety;
057import com.unboundid.util.ThreadSafetyLevel;
058
059import static com.unboundid.ldap.sdk.experimental.ExperimentalMessages.*;
060
061
062
063/**
064 * This class represents an entry that holds information about a search
065 * operation processed by an LDAP server, as per the specification described in
066 * draft-chu-ldap-logschema-00.
067 */
068@NotMutable()
069@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
070public final class DraftChuLDAPLogSchema00SearchEntry
071       extends DraftChuLDAPLogSchema00Entry
072{
073  /**
074   * The name of the attribute used to hold the alias dereference policy.
075   */
076  @NotNull public static final String ATTR_DEREFERENCE_POLICY =
077       "reqDerefAliases";
078
079
080
081  /**
082   * The name of the attribute used to hold the number of entries returned.
083   */
084  @NotNull public static final String ATTR_ENTRIES_RETURNED = "reqEntries";
085
086
087
088  /**
089   * The name of the attribute used to hold the search filter.
090   */
091  @NotNull public static final String ATTR_FILTER = "reqFilter";
092
093
094
095  /**
096   * The name of the attribute used to hold a requested attribute.
097   */
098  @NotNull public static final String ATTR_REQUESTED_ATTRIBUTE = "reqAttr";
099
100
101
102  /**
103   * The name of the attribute used to hold the search scope.
104   */
105  @NotNull public static final String ATTR_SCOPE = "reqScope";
106
107
108
109  /**
110   * The name of the attribute used to hold the requested size limit.
111   */
112  @NotNull public static final String ATTR_SIZE_LIMIT = "reqSizeLimit";
113
114
115
116  /**
117   * The name of the attribute used to hold the requested time limit in seconds.
118   */
119  @NotNull public static final String ATTR_TIME_LIMIT_SECONDS = "reqTimeLimit";
120
121
122
123  /**
124   * The name of the attribute used to hold the value of the typesOnly flag.
125   */
126  @NotNull public static final String ATTR_TYPES_ONLY = "reqAttrsOnly";
127
128
129
130  /**
131   * The serial version UID for this serializable class.
132   */
133  private static final long serialVersionUID = 948178493925578134L;
134
135
136
137  // The types only flag.
138  private final boolean typesOnly;
139
140  // The alias dereference policy.
141  @NotNull private final DereferencePolicy dereferencePolicy;
142
143  // The search filter.
144  @Nullable private final Filter filter;
145
146  // The number of entries returned.
147  @Nullable private final Integer entriesReturned;
148
149  // The requested size limit.
150  @Nullable private final Integer requestedSizeLimit;
151
152  // The requested time limit in seconds.
153  @Nullable private final Integer requestedTimeLimitSeconds;
154
155  // The list of requested attributes.
156  @NotNull private final List<String> requestedAttributes;
157
158  // The search scope.
159  @NotNull private final SearchScope scope;
160
161
162
163  /**
164   * Creates a new instance of this search access log entry from the provided
165   * entry.
166   *
167   * @param  entry  The entry used to create this search access log entry.
168   *
169   * @throws  LDAPException  If the provided entry cannot be decoded as a valid
170   *                         search access log entry as per the specification
171   *                         contained in draft-chu-ldap-logschema-00.
172   */
173  public DraftChuLDAPLogSchema00SearchEntry(@NotNull final Entry entry)
174         throws LDAPException
175  {
176    super(entry, OperationType.SEARCH);
177
178
179    // Get the scope.
180    final String scopeStr = entry.getAttributeValue(ATTR_SCOPE);
181    if (scopeStr == null)
182    {
183      throw new LDAPException(ResultCode.DECODING_ERROR,
184           ERR_LOGSCHEMA_DECODE_MISSING_REQUIRED_ATTR.get(entry.getDN(),
185                ATTR_SCOPE));
186    }
187
188    final String lowerScope = StaticUtils.toLowerCase(scopeStr);
189    if (lowerScope.equals("base"))
190    {
191      scope = SearchScope.BASE;
192    }
193    else if (lowerScope.equals("one"))
194    {
195      scope = SearchScope.ONE;
196    }
197    else if (lowerScope.equals("sub"))
198    {
199      scope = SearchScope.SUB;
200    }
201    else if (lowerScope.equals("subord"))
202    {
203      scope = SearchScope.SUBORDINATE_SUBTREE;
204    }
205    else
206    {
207      throw new LDAPException(ResultCode.DECODING_ERROR,
208           ERR_LOGSCHEMA_DECODE_SEARCH_SCOPE_ERROR.get(entry.getDN(),
209                ATTR_SCOPE, scopeStr));
210    }
211
212
213    // Get the dereference policy.
214    final String derefStr = entry.getAttributeValue(ATTR_DEREFERENCE_POLICY);
215    if (derefStr == null)
216    {
217      throw new LDAPException(ResultCode.DECODING_ERROR,
218           ERR_LOGSCHEMA_DECODE_MISSING_REQUIRED_ATTR.get(entry.getDN(),
219                ATTR_DEREFERENCE_POLICY));
220    }
221
222    final String lowerDeref = StaticUtils.toLowerCase(derefStr);
223    if (lowerDeref.equals("never"))
224    {
225      dereferencePolicy = DereferencePolicy.NEVER;
226    }
227    else if (lowerDeref.equals("searching"))
228    {
229      dereferencePolicy = DereferencePolicy.SEARCHING;
230    }
231    else if (lowerDeref.equals("finding"))
232    {
233      dereferencePolicy = DereferencePolicy.FINDING;
234    }
235    else if (lowerDeref.equals("always"))
236    {
237      dereferencePolicy = DereferencePolicy.ALWAYS;
238    }
239    else
240    {
241      throw new LDAPException(ResultCode.DECODING_ERROR,
242           ERR_LOGSCHEMA_DECODE_SEARCH_DEREF_ERROR.get(entry.getDN(),
243                ATTR_DEREFERENCE_POLICY, derefStr));
244    }
245
246
247    // Get the typesOnly flag.
248    final String typesOnlyStr = entry.getAttributeValue(ATTR_TYPES_ONLY);
249    if (typesOnlyStr == null)
250    {
251      throw new LDAPException(ResultCode.DECODING_ERROR,
252           ERR_LOGSCHEMA_DECODE_MISSING_REQUIRED_ATTR.get(entry.getDN(),
253                ATTR_TYPES_ONLY));
254    }
255
256    final String lowerTypesOnly = StaticUtils.toLowerCase(typesOnlyStr);
257    if (lowerTypesOnly.equals("true"))
258    {
259      typesOnly = true;
260    }
261    else if (lowerTypesOnly.equals("false"))
262    {
263      typesOnly = false;
264    }
265    else
266    {
267      throw new LDAPException(ResultCode.DECODING_ERROR,
268           ERR_LOGSCHEMA_DECODE_SEARCH_TYPES_ONLY_ERROR.get(entry.getDN(),
269                ATTR_TYPES_ONLY, typesOnlyStr));
270    }
271
272
273    // Get the filter.  For some strange reason, this is allowed to be
274    // undefined.
275    final String filterStr = entry.getAttributeValue(ATTR_FILTER);
276    if (filterStr == null)
277    {
278      filter = null;
279    }
280    else
281    {
282      try
283      {
284        filter = Filter.create(filterStr);
285      }
286      catch (final Exception e)
287      {
288        Debug.debugException(e);
289        throw new LDAPException(ResultCode.DECODING_ERROR,
290             ERR_LOGSCHEMA_DECODE_SEARCH_FILTER_ERROR.get(entry.getDN(),
291                  ATTR_FILTER, filterStr),
292             e);
293      }
294    }
295
296
297    // Get the set of requested attributes.
298    final String[] requestedAttrArray =
299         entry.getAttributeValues(ATTR_REQUESTED_ATTRIBUTE);
300    if ((requestedAttrArray == null) || (requestedAttrArray.length == 0))
301    {
302      requestedAttributes = Collections.emptyList();
303    }
304    else
305    {
306      requestedAttributes =
307           Collections.unmodifiableList(StaticUtils.toList(requestedAttrArray));
308    }
309
310
311    // Get the requested size limit.
312    final String sizeLimitStr = entry.getAttributeValue(ATTR_SIZE_LIMIT);
313    if (sizeLimitStr == null)
314    {
315      requestedSizeLimit = null;
316    }
317    else
318    {
319      try
320      {
321        requestedSizeLimit = Integer.parseInt(sizeLimitStr);
322      }
323      catch (final Exception e)
324      {
325        Debug.debugException(e);
326        throw new LDAPException(ResultCode.DECODING_ERROR,
327             ERR_LOGSCHEMA_DECODE_SEARCH_INT_ERROR.get(entry.getDN(),
328                  ATTR_SIZE_LIMIT, sizeLimitStr),
329             e);
330      }
331    }
332
333
334    // Get the requested time limit.
335    final String timeLimitStr =
336         entry.getAttributeValue(ATTR_TIME_LIMIT_SECONDS);
337    if (timeLimitStr == null)
338    {
339      requestedTimeLimitSeconds = null;
340    }
341    else
342    {
343      try
344      {
345        requestedTimeLimitSeconds = Integer.parseInt(timeLimitStr);
346      }
347      catch (final Exception e)
348      {
349        Debug.debugException(e);
350        throw new LDAPException(ResultCode.DECODING_ERROR,
351             ERR_LOGSCHEMA_DECODE_SEARCH_INT_ERROR.get(entry.getDN(),
352                  ATTR_TIME_LIMIT_SECONDS, timeLimitStr),
353             e);
354      }
355    }
356
357
358    // Get the number of entries returned.
359    final String entriesReturnedStr =
360         entry.getAttributeValue(ATTR_ENTRIES_RETURNED);
361    if (entriesReturnedStr == null)
362    {
363      entriesReturned = null;
364    }
365    else
366    {
367      try
368      {
369        entriesReturned = Integer.parseInt(entriesReturnedStr);
370      }
371      catch (final Exception e)
372      {
373        Debug.debugException(e);
374        throw new LDAPException(ResultCode.DECODING_ERROR,
375             ERR_LOGSCHEMA_DECODE_SEARCH_INT_ERROR.get(entry.getDN(),
376                  ATTR_ENTRIES_RETURNED, entriesReturnedStr),
377             e);
378      }
379    }
380  }
381
382
383
384  /**
385   * Retrieves the scope for the search request described by this search access
386   * log entry.
387   *
388   * @return  The scope for the search request described by this search access
389   *          log entry.
390   */
391  @NotNull()
392  public SearchScope getScope()
393  {
394    return scope;
395  }
396
397
398
399  /**
400   * Retrieves the alias dereference policy for the search request described by
401   * this search access log entry.
402   *
403   * @return  The alias dereference policy for the search request described by
404   *          this search access log entry.
405   */
406  @NotNull()
407  public DereferencePolicy getDereferencePolicy()
408  {
409    return dereferencePolicy;
410  }
411
412
413
414  /**
415   * Retrieves the value of the typesOnly flag for the search request described
416   * by this search access log entry.
417   *
418   * @return  The value of the typesOnly flag for the search request described
419   *          by this search access log entry.
420   */
421  public boolean typesOnly()
422  {
423    return typesOnly;
424  }
425
426
427
428  /**
429   * Retrieves the filter for the search request described by this search access
430   * log entry, if available.
431   *
432   * @return  The filter for the search request described by this search access
433   *          log entry, or {@code null} if no filter was included in the access
434   *          log entry.
435   */
436  @Nullable()
437  public Filter getFilter()
438  {
439    return filter;
440  }
441
442
443
444  /**
445   * Retrieves the requested size limit for the search request described by this
446   * search access log entry, if available.
447   *
448   * @return  The requested size limit for the search request described by this
449   *          search access log entry, or {@code null} if no size limit was
450   *          included in the access log entry.
451   */
452  @Nullable()
453  public Integer getRequestedSizeLimit()
454  {
455    return requestedSizeLimit;
456  }
457
458
459
460  /**
461   * Retrieves the requested time limit (in seconds) for the search request
462   * described by this search access log entry, if available.
463   *
464   * @return  The requested time limit (in seconds) for the search request
465   *          described by this search access log entry, or {@code null} if no
466   *          time limit was included in the access log entry.
467   */
468  @Nullable()
469  public Integer getRequestedTimeLimitSeconds()
470  {
471    return requestedTimeLimitSeconds;
472  }
473
474
475
476  /**
477   * Retrieves the requested attributes for the search request described by this
478   * search access log entry, if available.
479   *
480   * @return  The requested attributes for the search request described by this
481   *          search access log entry, or an empty list if no requested
482   *          attributes were included in the access log entry.
483   */
484  @NotNull()
485  public List<String> getRequestedAttributes()
486  {
487    return requestedAttributes;
488  }
489
490
491
492  /**
493   * Retrieves the number of entries returned to the client in response to the
494   * search request described by this search access log entry, if available.
495   *
496   * @return  The number of entries returned to the client in response to the
497   *          search request described by this search access log entry, or
498   *          {@code null} if the number of entries returned was not included in
499   *          the access log entry.
500   */
501  @Nullable()
502  public Integer getEntriesReturned()
503  {
504    return entriesReturned;
505  }
506
507
508
509  /**
510   * Retrieves a {@code SearchRequest} created from this search access log
511   * entry.  If the size limit or time limit was not present in the entry, a
512   * default of zero will be used.  If the filter was not present in the entry,
513   * a default of "(objectClass=*)" will be used.
514   *
515   * @return  The {@code SearchRequest} created from this search access log
516   *          entry.
517   */
518  @NotNull()
519  public SearchRequest toSearchRequest()
520  {
521    final int sizeLimit =
522         ((requestedSizeLimit == null)
523              ? 0
524              : requestedSizeLimit);
525    final int timeLimit =
526         ((requestedTimeLimitSeconds == null)
527              ? 0
528              : requestedTimeLimitSeconds);
529    final Filter f =
530         ((filter == null)
531              ? Filter.createPresenceFilter("objectClass")
532              : filter);
533
534    final String[] attrArray =
535         requestedAttributes.toArray(StaticUtils.NO_STRINGS);
536
537    final SearchRequest searchRequest = new SearchRequest(getTargetEntryDN(),
538         scope, dereferencePolicy, sizeLimit, timeLimit, typesOnly, f,
539         attrArray);
540    searchRequest.setControls(getRequestControlArray());
541    return searchRequest;
542  }
543}