001/*
002 * Copyright 2011-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.tasks;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collections;
028import java.util.Date;
029import java.util.LinkedHashMap;
030import java.util.LinkedList;
031import java.util.List;
032import java.util.Map;
033
034import com.unboundid.ldap.sdk.Attribute;
035import com.unboundid.ldap.sdk.Entry;
036import com.unboundid.ldap.sdk.Filter;
037import com.unboundid.ldap.sdk.LDAPException;
038import com.unboundid.util.NotMutable;
039import com.unboundid.util.StaticUtils;
040import com.unboundid.util.ThreadSafety;
041import com.unboundid.util.ThreadSafetyLevel;
042
043import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*;
044import static com.unboundid.util.Validator.*;
045
046
047
048/**
049 * This class defines a Directory Server task that can be used to cause the
050 * server to initiate a data security audit, which can look for potential
051 * issues in the environment that can impact the security of the directory
052 * environment.
053 * <BR>
054 * <BLOCKQUOTE>
055 *   <B>NOTE:</B>  This class, and other classes within the
056 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
057 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
058 *   server products.  These classes provide support for proprietary
059 *   functionality or for external specifications that are not considered stable
060 *   or mature enough to be guaranteed to work in an interoperable way with
061 *   other types of LDAP servers.
062 * </BLOCKQUOTE>
063 * <BR>
064 * The properties that are available for use with this type of task include:
065 * <UL>
066 *   <LI>The names of the auditors to include or exclude from the audit.  This
067 *       is optional, and if it is not provided, then all enabled auditors will
068 *       be used.</LI>
069 *   <LI>The backend IDs for the backends containing the data to be audited.
070 *       This is optional, and if it is not provided then the server will run
071 *       the audit in all backends that support this capability.</LI>
072 *   <LI>A set of filters which identify the entries that should be examined by
073 *       the audit.  This is optional, and if it is not provided, then all
074 *       entries in the selected backends will be included.</LI>
075 *   <LI>The path to the directory in which the output files should be
076 *       generated.  This is optional, and if it is not provided then the server
077 *       will use a default output directory.</LI>
078 * </UL>
079 */
080@NotMutable()
081@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
082public final class AuditDataSecurityTask
083       extends Task
084{
085  /**
086   * The fully-qualified name of the Java class that is used for the audit data
087   * security task.
088   */
089  static final String AUDIT_DATA_SECURITY_TASK_CLASS =
090       "com.unboundid.directory.server.tasks.AuditDataSecurityTask";
091
092
093
094  /**
095   * The name of the attribute used to specify the set of auditors to use to
096   * examine the data.
097   */
098  private static final String ATTR_INCLUDE_AUDITOR =
099       "ds-task-audit-data-security-include-auditor";
100
101
102
103  /**
104   * The name of the attribute used to specify the set of auditors that should
105   * not be used when examining the data.
106   */
107  private static final String ATTR_EXCLUDE_AUDITOR =
108       "ds-task-audit-data-security-exclude-auditor";
109
110
111
112  /**
113   * The name of the attribute used to the backend IDs for the backends in which
114   * the audit should be performed.
115   */
116  private static final String ATTR_BACKEND_ID =
117       "ds-task-audit-data-security-backend-id";
118
119
120
121  /**
122   * The name of the attribute used to specify a set of filters that should be
123   * used to identify entries to include in the audit.
124   */
125  private static final String ATTR_REPORT_FILTER =
126       "ds-task-audit-data-security-report-filter";
127
128
129
130  /**
131   * The name of the attribute used to specify the directory in which the report
132   * output files should be written.
133   */
134  private static final String ATTR_OUTPUT_DIRECTORY =
135       "ds-task-audit-data-security-output-directory";
136
137
138
139  /**
140   * The name of the object class used in audit data security task entries.
141   */
142  private static final String OC_AUDIT_DATA_SECURITY_TASK =
143       "ds-task-audit-data-security";
144
145
146
147  /**
148   * The task property that will be used for the included set of auditors.
149   */
150  private static final TaskProperty PROPERTY_INCLUDE_AUDITOR =
151       new TaskProperty(ATTR_INCLUDE_AUDITOR,
152            INFO_AUDIT_DATA_SECURITY_DISPLAY_NAME_INCLUDE_AUDITOR.get(),
153            INFO_AUDIT_DATA_SECURITY_DESCRIPTION_INCLUDE_AUDITOR.get(),
154            String.class, false, true, false);
155
156
157
158  /**
159   * The task property that will be used for the excluded set of auditors.
160   */
161  private static final TaskProperty PROPERTY_EXCLUDE_AUDITOR =
162       new TaskProperty(ATTR_EXCLUDE_AUDITOR,
163            INFO_AUDIT_DATA_SECURITY_DISPLAY_NAME_EXCLUDE_AUDITOR.get(),
164            INFO_AUDIT_DATA_SECURITY_DESCRIPTION_EXCLUDE_AUDITOR.get(),
165            String.class, false, true, false);
166
167
168
169  /**
170   * The task property that will be used for the backend IDs.
171   */
172  private static final TaskProperty PROPERTY_BACKEND_ID =
173       new TaskProperty(ATTR_BACKEND_ID,
174            INFO_AUDIT_DATA_SECURITY_DISPLAY_NAME_BACKEND_ID.get(),
175            INFO_AUDIT_DATA_SECURITY_DESCRIPTION_BACKEND_ID.get(),
176            String.class, false, true, false);
177
178
179
180  /**
181   * The task property that will be used for the report filters.
182   */
183  private static final TaskProperty PROPERTY_REPORT_FILTER =
184       new TaskProperty(ATTR_REPORT_FILTER,
185            INFO_AUDIT_DATA_SECURITY_DISPLAY_NAME_REPORT_FILTER.get(),
186            INFO_AUDIT_DATA_SECURITY_DESCRIPTION_REPORT_FILTER.get(),
187            String.class, false, true, false);
188
189
190
191  /**
192   * The task property that will be used for the output directory.
193   */
194  private static final TaskProperty PROPERTY_OUTPUT_DIRECTORY =
195       new TaskProperty(ATTR_OUTPUT_DIRECTORY,
196            INFO_AUDIT_DATA_SECURITY_DISPLAY_NAME_OUTPUT_DIR.get(),
197            INFO_AUDIT_DATA_SECURITY_DESCRIPTION_OUTPUT_DIR.get(),
198            String.class, false, false, false);
199
200
201
202  /**
203   * The serial version UID for this serializable class.
204   */
205  private static final long serialVersionUID = -4994621474763299632L;
206
207
208
209  // The backend IDs of the backends in which the audit should be performed.
210  private final List<String> backendIDs;
211
212  // The names of the excluded auditors to use in the audit.
213  private final List<String> excludeAuditors;
214
215  // The names of the included auditors to use in the audit.
216  private final List<String> includeAuditors;
217
218  // The report filters to select entries to audit.
219  private final List<String> reportFilters;
220
221  // The path of the output directory to use for report data files.
222  private final String outputDirectory;
223
224
225
226  /**
227   * Creates a new uninitialized audit data security task instance which should
228   * only be used for obtaining general information about this task, including
229   * the task name, description, and supported properties.  Attempts to use a
230   * task created with this constructor for any other reason will likely fail.
231   */
232  public AuditDataSecurityTask()
233  {
234    excludeAuditors = null;
235    includeAuditors = null;
236    backendIDs      = null;
237    reportFilters   = null;
238    outputDirectory = null;
239  }
240
241
242
243  /**
244   * Creates a new audit data security task with the provided information and
245   * default settings for all general task properties.
246   *
247   * @param  includeAuditors  The names of the auditors that should be used to
248   *                          examine the data.  It may be {@code null} or empty
249   *                          if an exclude list should be provided, or if all
250   *                          enabled auditors should be invoked.  You must not
251   *                          provide both include and exclude auditors.
252   * @param  excludeAuditors  The names of the auditors that should be excluded
253   *                          when examining the data.  It may be {@code null}
254   *                          or empty if an include list should be provided, or
255   *                          if all enabled auditors should be invoked.  You
256   *                          must not provide both include and exclude
257   *                          auditors.
258   * @param  backendIDs       The backend IDs of the backends containing the
259   *                          data to examine.  It may be {@code null} or empty
260   *                          if all supported backends should be selected.
261   * @param  reportFilters    A set of filters which identify entries that
262   *                          should be examined.  It may be {@code null} or
263   *                          empty if all entries should be examined.
264   * @param  outputDirectory  The path to the output directory (on the server
265   *                          filesystem) in which report data files should be
266   *                          written.  It may be {@code null} if a default
267   *                          output directory should be used.
268   */
269  public AuditDataSecurityTask(final List<String> includeAuditors,
270                               final List<String> excludeAuditors,
271                               final List<String> backendIDs,
272                               final List<String> reportFilters,
273                               final String outputDirectory)
274  {
275    this(null, includeAuditors, excludeAuditors, backendIDs, reportFilters,
276         outputDirectory, null, null, null, null, null);
277  }
278
279
280
281  /**
282   * Creates a new audit data security task with the provided information.
283   *
284   * @param  taskID                  The task ID to use for this task.  If it is
285   *                                 {@code null} then a UUID will be generated
286   *                                 for use as the task ID.
287   * @param  includeAuditors         The names of the auditors that should be
288   *                                 used to examine the data.  It may be
289   *                                 {@code null} or empty if an exclude list
290   *                                 should be provided, or if all enabled
291   *                                 auditors should be invoked.  You must not
292   *                                 provide both include and exclude auditors.
293   * @param  excludeAuditors         The names of the auditors that should be
294   *                                 excluded when examining the data.  It may
295   *                                 be {@code null} or empty if an include list
296   *                                 should be provided, or if all enabled
297   *                                 auditors should be invoked.  You must not
298   *                                 provide both include and exclude auditors.
299   * @param  backendIDs              The backend IDs of the backends containing
300   *                                 the data to examine.  It may be
301   *                                 {@code null} or empty if all supported
302   *                                 backends should be selected.
303   * @param  reportFilters           A set of filters which identify entries
304   *                                 that should be examined.  It may be
305   *                                 {@code null} or empty if all entries should
306   *                                 be examined.
307   * @param  outputDirectory         The path to the output directory (on the
308   *                                 server filesystem) in which report data
309   *                                 files should be written.  It may be
310   *                                 {@code null} if a default output directory
311   *                                 should be used.
312   * @param  scheduledStartTime      The time that this task should start
313   *                                 running.
314   * @param  dependencyIDs           The list of task IDs that will be required
315   *                                 to complete before this task will be
316   *                                 eligible to start.
317   * @param  failedDependencyAction  Indicates what action should be taken if
318   *                                 any of the dependencies for this task do
319   *                                 not complete successfully.
320   * @param  notifyOnCompletion      The list of e-mail addresses of individuals
321   *                                 that should be notified when this task
322   *                                 completes.
323   * @param  notifyOnError           The list of e-mail addresses of individuals
324   *                                 that should be notified if this task does
325   *                                 not complete successfully.
326   */
327  public AuditDataSecurityTask(final String taskID,
328              final List<String> includeAuditors,
329              final List<String> excludeAuditors, final List<String> backendIDs,
330              final List<String> reportFilters, final String outputDirectory,
331              final Date scheduledStartTime, final List<String> dependencyIDs,
332              final FailedDependencyAction failedDependencyAction,
333              final List<String> notifyOnCompletion,
334              final List<String> notifyOnError)
335  {
336    super(taskID, AUDIT_DATA_SECURITY_TASK_CLASS, scheduledStartTime,
337         dependencyIDs, failedDependencyAction, notifyOnCompletion,
338         notifyOnError);
339
340    this.includeAuditors = getStringList(includeAuditors);
341    this.excludeAuditors = getStringList(excludeAuditors);
342    this.backendIDs      = getStringList(backendIDs);
343    this.reportFilters   = getStringList(reportFilters);
344    this.outputDirectory = outputDirectory;
345
346    ensureTrue(
347         (this.includeAuditors.isEmpty() || this.excludeAuditors.isEmpty()),
348         "You cannot request both include and exclude auditors.");
349  }
350
351
352
353  /**
354   * Creates a new audit data security task from the provided entry.
355   *
356   * @param  entry  The entry to use to create this audit data security task.
357   *
358   * @throws  TaskException  If the provided entry cannot be parsed as an audit
359   *                         data security task entry.
360   */
361  public AuditDataSecurityTask(final Entry entry)
362         throws TaskException
363  {
364    super(entry);
365
366    includeAuditors = Collections.unmodifiableList(StaticUtils.toNonNullList(
367         entry.getAttributeValues(ATTR_INCLUDE_AUDITOR)));
368    excludeAuditors = Collections.unmodifiableList(StaticUtils.toNonNullList(
369         entry.getAttributeValues(ATTR_EXCLUDE_AUDITOR)));
370    backendIDs = Collections.unmodifiableList(StaticUtils.toNonNullList(
371         entry.getAttributeValues(ATTR_BACKEND_ID)));
372    reportFilters = Collections.unmodifiableList(StaticUtils.toNonNullList(
373         entry.getAttributeValues(ATTR_REPORT_FILTER)));
374    outputDirectory = entry.getAttributeValue(ATTR_OUTPUT_DIRECTORY);
375  }
376
377
378
379  /**
380   * Creates a new audit data security task from the provided set of task
381   * properties.
382   *
383   * @param  properties  The set of task properties and their corresponding
384   *                     values to use for the task.  It must not be
385   *                     {@code null}.
386   *
387   * @throws  TaskException  If the provided set of properties cannot be used to
388   *                         create a valid audit data security task.
389   */
390  public AuditDataSecurityTask(final Map<TaskProperty,List<Object>> properties)
391         throws TaskException
392  {
393    super(AUDIT_DATA_SECURITY_TASK_CLASS, properties);
394
395    String outputDir = null;
396    final LinkedList<String> includeAuditorsList = new LinkedList<String>();
397    final LinkedList<String> excludeAuditorsList = new LinkedList<String>();
398    final LinkedList<String> backendIDList       = new LinkedList<String>();
399    final LinkedList<String> reportFilterList    = new LinkedList<String>();
400    for (final Map.Entry<TaskProperty,List<Object>> entry :
401         properties.entrySet())
402    {
403      final TaskProperty p = entry.getKey();
404      final String attrName = StaticUtils.toLowerCase(p.getAttributeName());
405      final List<Object> values = entry.getValue();
406
407      if (attrName.equals(ATTR_INCLUDE_AUDITOR))
408      {
409        final String[] s = parseStrings(p, values, null);
410        if (s != null)
411        {
412          includeAuditorsList.addAll(Arrays.asList(s));
413        }
414      }
415      else if (attrName.equals(ATTR_EXCLUDE_AUDITOR))
416      {
417        final String[] s = parseStrings(p, values, null);
418        if (s != null)
419        {
420          excludeAuditorsList.addAll(Arrays.asList(s));
421        }
422      }
423      else if (attrName.equals(ATTR_BACKEND_ID))
424      {
425        final String[] s = parseStrings(p, values, null);
426        if (s != null)
427        {
428          backendIDList.addAll(Arrays.asList(s));
429        }
430      }
431      else if (attrName.equals(ATTR_REPORT_FILTER))
432      {
433        final String[] s = parseStrings(p, values, null);
434        if (s != null)
435        {
436          reportFilterList.addAll(Arrays.asList(s));
437        }
438      }
439      else if (attrName.equals(ATTR_OUTPUT_DIRECTORY))
440      {
441        outputDir = parseString(p, values, null);
442      }
443    }
444
445    includeAuditors = Collections.unmodifiableList(includeAuditorsList);
446    excludeAuditors = Collections.unmodifiableList(excludeAuditorsList);
447    backendIDs      = Collections.unmodifiableList(backendIDList);
448    reportFilters   = Collections.unmodifiableList(reportFilterList);
449    outputDirectory = outputDir;
450
451    if ((! includeAuditors.isEmpty()) && (! excludeAuditors.isEmpty()))
452    {
453      throw new TaskException(
454           ERR_AUDIT_DATA_SECURITY_BOTH_INCLUDE_AND_EXCLUDE_AUDITORS.get());
455    }
456  }
457
458
459
460  /**
461   * {@inheritDoc}
462   */
463  @Override()
464  public String getTaskName()
465  {
466    return INFO_TASK_NAME_AUDIT_DATA_SECURITY.get();
467  }
468
469
470
471  /**
472   * {@inheritDoc}
473   */
474  @Override()
475  public String getTaskDescription()
476  {
477    return INFO_TASK_DESCRIPTION_AUDIT_DATA_SECURITY.get();
478  }
479
480
481
482  /**
483   * Retrieves the names of the auditors that should be invoked during the
484   * data security audit.
485   *
486   * @return  The names of the include auditors that should be used for the
487   *          task, or an empty list if either an exclude list should be used or
488   *          all enabled auditors should be used.
489   */
490  public List<String> getIncludeAuditors()
491  {
492    return includeAuditors;
493  }
494
495
496
497  /**
498   * Retrieves the names of the auditors that should not be invoked during the
499   * audit.
500   *
501   * @return  The names of the exclude auditors that should be used for the
502   *          task, or an empty list if either an include list should be used or
503   *          all enabled auditors should be used.
504   */
505  public List<String> getExcludeAuditors()
506  {
507    return excludeAuditors;
508  }
509
510
511
512  /**
513   * Retrieves the backend IDs of the backends that should be examined during
514   * the course of the audit.
515   *
516   * @return  The backend IDs of the backends that should be examined during the
517   *          course of the audit, or an empty list if all backends that support
518   *          this capability should be used.
519   */
520  public List<String> getBackendIDs()
521  {
522    return backendIDs;
523  }
524
525
526
527  /**
528   * Retrieves the string representations of the report filters that should be
529   * used to identify which entries should be examined during the course of the
530   * audit.
531   *
532   * @return  The string representations of the report filters that should be
533   *          used to identify which entries should be examined during the
534   *          course of the audit, or an empty list if all entries should be
535   *          examined.
536   */
537  public List<String> getReportFilterStrings()
538  {
539    return reportFilters;
540  }
541
542
543
544  /**
545   * Retrieves the parsed report filters that should be used to identify which
546   * entries should be examined during the course of the audit.
547   *
548   * @return  The parsed report filters that should be used to identify which
549   *          entries should be examined during the course of the audit, or an
550   *          empty list if all entries should be examined.
551   *
552   * @throws  LDAPException  If any of the filter strings cannot be parsed as a
553   *                         valid filter.
554   */
555  public List<Filter> getReportFilters()
556         throws LDAPException
557  {
558    if (reportFilters.isEmpty())
559    {
560      return Collections.emptyList();
561    }
562
563    final ArrayList<Filter> filterList =
564         new ArrayList<Filter>(reportFilters.size());
565    for (final String filter : reportFilters)
566    {
567      filterList.add(Filter.create(filter));
568    }
569    return Collections.unmodifiableList(filterList);
570  }
571
572
573
574  /**
575   * Retrieves the path to the directory on the server filesystem in which the
576   * report output files should be written.
577   *
578   * @return  The path to the directory on the server filesystem in which the
579   *          report output files should be written.
580   */
581  public String getOutputDirectory()
582  {
583    return outputDirectory;
584  }
585
586
587
588  /**
589   * {@inheritDoc}
590   */
591  @Override()
592  protected List<String> getAdditionalObjectClasses()
593  {
594    return Arrays.asList(OC_AUDIT_DATA_SECURITY_TASK);
595  }
596
597
598
599  /**
600   * {@inheritDoc}
601   */
602  @Override()
603  protected List<Attribute> getAdditionalAttributes()
604  {
605    final LinkedList<Attribute> attrList = new LinkedList<Attribute>();
606
607    if (! includeAuditors.isEmpty())
608    {
609      attrList.add(new Attribute(ATTR_INCLUDE_AUDITOR, includeAuditors));
610    }
611
612    if (! excludeAuditors.isEmpty())
613    {
614      attrList.add(new Attribute(ATTR_EXCLUDE_AUDITOR, excludeAuditors));
615    }
616
617    if (! backendIDs.isEmpty())
618    {
619      attrList.add(new Attribute(ATTR_BACKEND_ID, backendIDs));
620    }
621
622    if (! reportFilters.isEmpty())
623    {
624      attrList.add(new Attribute(ATTR_REPORT_FILTER, reportFilters));
625    }
626
627    if (outputDirectory != null)
628    {
629      attrList.add(new Attribute(ATTR_OUTPUT_DIRECTORY, outputDirectory));
630    }
631
632    return attrList;
633  }
634
635
636
637  /**
638   * {@inheritDoc}
639   */
640  @Override()
641  public List<TaskProperty> getTaskSpecificProperties()
642  {
643    return Collections.unmodifiableList(Arrays.asList(
644         PROPERTY_INCLUDE_AUDITOR,
645         PROPERTY_EXCLUDE_AUDITOR,
646         PROPERTY_BACKEND_ID,
647         PROPERTY_REPORT_FILTER,
648         PROPERTY_OUTPUT_DIRECTORY));
649  }
650
651
652
653  /**
654   * {@inheritDoc}
655   */
656  @Override()
657  public Map<TaskProperty,List<Object>> getTaskPropertyValues()
658  {
659    final LinkedHashMap<TaskProperty,List<Object>> props =
660         new LinkedHashMap<TaskProperty,List<Object>>(5);
661
662    if (! includeAuditors.isEmpty())
663    {
664      props.put(PROPERTY_INCLUDE_AUDITOR,
665           Collections.<Object>unmodifiableList(includeAuditors));
666    }
667
668    if (! excludeAuditors.isEmpty())
669    {
670      props.put(PROPERTY_EXCLUDE_AUDITOR,
671           Collections.<Object>unmodifiableList(excludeAuditors));
672    }
673
674    if (! backendIDs.isEmpty())
675    {
676      props.put(PROPERTY_BACKEND_ID,
677           Collections.<Object>unmodifiableList(backendIDs));
678    }
679
680    if (! reportFilters.isEmpty())
681    {
682      props.put(PROPERTY_REPORT_FILTER,
683           Collections.<Object>unmodifiableList(reportFilters));
684    }
685
686    if (outputDirectory != null)
687    {
688      props.put(PROPERTY_OUTPUT_DIRECTORY,
689           Collections.<Object>unmodifiableList(Arrays.asList(
690                outputDirectory)));
691    }
692
693    return Collections.unmodifiableMap(props);
694  }
695
696
697
698  /**
699   * Retrieves an unmodifiable list using information from the provided list.
700   * If the given list is {@code null}, then an empty list will be returned.
701   * Otherwise, an unmodifiable version of the provided list will be returned.
702   *
703   * @param  l  The list to be processed.
704   *
705   * @return  The resulting string list.
706   */
707  private static List<String> getStringList(final List<String> l)
708  {
709    if (l == null)
710    {
711      return Collections.emptyList();
712    }
713    else
714    {
715      return Collections.unmodifiableList(l);
716    }
717  }
718}