All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.schema.EntryValidator Maven / Gradle / Ivy

/*
 * Copyright 2008-2024 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2008-2024 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2008-2024 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.schema;



import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;

import com.unboundid.asn1.ASN1OctetString;
import com.unboundid.ldap.matchingrules.MatchingRule;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.RDN;
import com.unboundid.util.Debug;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.Validator;

import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;



/**
 * This class provides a mechanism for validating entries against a schema.  It
 * provides the ability to customize the types of validation to perform, and can
 * collect information about the entries that fail validation to provide a
 * summary of the problems encountered.
 * 

* The types of validation that may be performed for each entry include: *
    *
  • Ensure that the entry has a valid DN.
  • *
  • Ensure that all attribute values used in the entry's RDN are also * present in the entry.
  • *
  • Ensure that the entry has exactly one structural object class.
  • *
  • Ensure that all of the object classes for the entry are defined in the * schema.
  • *
  • Ensure that all of the auxiliary classes for the entry are allowed by * the DIT content rule for the entry's structural object class (if such a * DIT content rule is defined).
  • *
  • Ensure that all attributes contained in the entry are defined in the * schema.
  • *
  • Ensure that all attributes required by the entry's object classes or * DIT content rule (if defined) are present in the entry.
  • *
  • Ensure that all of the user attributes contained in the entry are * allowed by the entry's object classes or DIT content rule (if * defined).
  • *
  • Ensure that all attribute values conform to the requirements of the * associated attribute syntax.
  • *
  • Ensure that all attributes with multiple values are defined as * multi-valued in the associated schema.
  • *
  • If there is a name form associated with the entry's structural object * class, then ensure that the entry's RDN satisfies its constraints.
  • *
* All of these forms of validation will be performed by default, but individual * types of validation may be enabled or disabled. *

* This class will not make any attempt to validate compliance with DIT * structure rules, nor will it check the OBSOLETE field for any of the schema * elements. In addition, attempts to validate whether attribute values * conform to the syntax for the associated attribute type may only be * completely accurate for syntaxes supported by the LDAP SDK. *

* This class is largely threadsafe, and the {@link EntryValidator#entryIsValid} * is designed so that it can be invoked concurrently by multiple threads. * Note, however, that it is not recommended that the any of the other methods * in this class be used while any threads are running the {@code entryIsValid} * method because changing the configuration or attempting to retrieve retrieve * information may yield inaccurate or inconsistent results. */ @ThreadSafety(level=ThreadSafetyLevel.MOSTLY_THREADSAFE) public final class EntryValidator implements Serializable { /** * The serial version UID for this serializable class. */ private static final long serialVersionUID = -8945609557086398241L; // A count of the total number of entries examined. @NotNull private final AtomicLong entriesExamined; // A count of the number of entries missing an attribute value contained in // the RDN. @NotNull private final AtomicLong entriesMissingRDNValues; // A count of the total number of invalid entries encountered. @NotNull private final AtomicLong invalidEntries; // A count of the number of entries with DNs that could not be parsed. @NotNull private final AtomicLong malformedDNs; // A count of the number of entries missing a superior object class. @NotNull private final AtomicLong missingSuperiorClasses; // A count of the number of entries containing multiple structural object // classes. @NotNull private final AtomicLong multipleStructuralClasses; // A count of the number of entries with RDNs that violate the associated // name form. @NotNull private final AtomicLong nameFormViolations; // A count of the number of entries without any object class. @NotNull private final AtomicLong noObjectClasses; // A count of the number of entries without a structural object class. @NotNull private final AtomicLong noStructuralClass; // Indicates whether an entry should be considered invalid if it contains an // attribute value which violates the associated attribute syntax. private boolean checkAttributeSyntax; // Indicates whether an entry should be considered invalid if it contains one // or more attribute values in its RDN that are not present in the set of // entry attributes. private boolean checkEntryMissingRDNValues; // Indicates whether an entry should be considered invalid if its DN cannot be // parsed. private boolean checkMalformedDNs; // Indicates whether an entry should be considered invalid if it is missing // attributes required by its object classes or DIT content rule. private boolean checkMissingAttributes; // Indicates whether an entry should be considered invalid if it is missing // one or more superior object classes. private boolean checkMissingSuperiorObjectClasses; // Indicates whether an entry should be considered invalid if its RDN does not // conform to name form requirements. private boolean checkNameForms; // Indicates whether an entry should be considered invalid if it contains any // attributes which are not allowed by its object classes or DIT content rule. private boolean checkProhibitedAttributes; // Indicates whether an entry should be considered invalid if it contains an // auxiliary class that is not allowed by its DIT content rule or an abstract // class that is not associated with a non-abstract class. private boolean checkProhibitedObjectClasses; // Indicates whether an entry should be considered invalid if it contains any // attribute defined as single-valued with more than one values. private boolean checkSingleValuedAttributes; // Indicates whether an entry should be considered invalid if it does not // contain exactly one structural object class. private boolean checkStructuralObjectClasses; // Indicates whether an entry should be considered invalid if it contains an // attribute which is not defined in the schema. private boolean checkUndefinedAttributes; // Indicates whether an entry should be considered invalid if it contains an // object class which is not defined in the schema. private boolean checkUndefinedObjectClasses; // A map of the attributes with values violating the associated syntax to the // number of values found violating the syntax. @NotNull private final ConcurrentHashMap attributesViolatingSyntax; // A map of the required attribute types that were missing from entries to // the number of entries missing them. @NotNull private final ConcurrentHashMap missingAttributes; // A map of the prohibited attribute types that were included in entries to // the number of entries referencing them. @NotNull private final ConcurrentHashMap prohibitedAttributes; // A map of the prohibited auxiliary object classes that were included in // entries to the number of entries referencing them. @NotNull private final ConcurrentHashMap prohibitedObjectClasses; // A map of the single-valued attributes with multiple values to the number // of entries with multiple values for those attributes. @NotNull private final ConcurrentHashMap singleValueViolations; // A map of undefined attribute types to the number of entries referencing // them. @NotNull private final ConcurrentHashMap undefinedAttributes; // A map of undefined object classes to the number of entries referencing // them. @NotNull private final ConcurrentHashMap undefinedObjectClasses; // The schema against which entries will be validated. @NotNull private final Schema schema; // The attribute types for which to ignore syntax violations. @NotNull private Set ignoreSyntaxViolationTypes; /** * Creates a new entry validator that will validate entries according to the * provided schema. * * @param schema The schema against which entries will be validated. */ public EntryValidator(@NotNull final Schema schema) { this.schema = schema; checkAttributeSyntax = true; checkEntryMissingRDNValues = true; checkMalformedDNs = true; checkMissingAttributes = true; checkMissingSuperiorObjectClasses = true; checkNameForms = true; checkProhibitedAttributes = true; checkProhibitedObjectClasses = true; checkSingleValuedAttributes = true; checkStructuralObjectClasses = true; checkUndefinedAttributes = true; checkUndefinedObjectClasses = true; ignoreSyntaxViolationTypes = Collections.emptySet(); entriesExamined = new AtomicLong(0L); entriesMissingRDNValues = new AtomicLong(0L); invalidEntries = new AtomicLong(0L); malformedDNs = new AtomicLong(0L); missingSuperiorClasses = new AtomicLong(0L); multipleStructuralClasses = new AtomicLong(0L); nameFormViolations = new AtomicLong(0L); noObjectClasses = new AtomicLong(0L); noStructuralClass = new AtomicLong(0L); attributesViolatingSyntax = new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); missingAttributes = new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); prohibitedAttributes = new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); prohibitedObjectClasses = new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); singleValueViolations = new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); undefinedAttributes = new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); undefinedObjectClasses = new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); } /** * Indicates whether the entry validator should consider entries invalid if * they are missing attributes which are required by the object classes or * DIT content rule (if applicable) for the entry. * * @return {@code true} if entries that are missing attributes required by * its object classes or DIT content rule should be considered * invalid, or {@code false} if not. */ public boolean checkMissingAttributes() { return checkMissingAttributes; } /** * Specifies whether the entry validator should consider entries invalid if * they are missing attributes which are required by the object classes or DIT * content rule (if applicable) for the entry. * * @param checkMissingAttributes Indicates whether the entry validator * should consider entries invalid if they are * missing required attributes. */ public void setCheckMissingAttributes(final boolean checkMissingAttributes) { this.checkMissingAttributes = checkMissingAttributes; } /** * Indicates whether the entry validator should consider entries invalid if * they are missing any superior classes for the included set of object * classes. * * @return {@code true} if entries that are missing superior classes should * be considered invalid, or {@code false} if not. */ public boolean checkMissingSuperiorObjectClasses() { return checkMissingSuperiorObjectClasses; } /** * Specifies whether the entry validator should consider entries invalid if * they are missing any superior classes for the included set of object * classes. * * @param checkMissingSuperiorObjectClasses Indicates whether the entry * validator should consider * entries invalid if they are * missing any superior classes for * the included set of object * classes. */ public void setCheckMissingSuperiorObjectClasses( final boolean checkMissingSuperiorObjectClasses) { this.checkMissingSuperiorObjectClasses = checkMissingSuperiorObjectClasses; } /** * Indicates whether the entry validator should consider entries invalid if * their DNs cannot be parsed. * * @return {@code true} if entries with malformed DNs should be considered * invalid, or {@code false} if not. */ public boolean checkMalformedDNs() { return checkMalformedDNs; } /** * Specifies whether the entry validator should consider entries invalid if * their DNs cannot be parsed. * * @param checkMalformedDNs Specifies whether entries with malformed DNs * should be considered invalid. */ public void setCheckMalformedDNs(final boolean checkMalformedDNs) { this.checkMalformedDNs = checkMalformedDNs; } /** * Indicates whether the entry validator should consider entries invalid if * they contain one or more attribute values in their RDN that are not present * in the set of entry attributes. * * @return {@code true} if entries missing one or more attribute values * included in their RDNs should be considered invalid, or * {@code false} if not. */ public boolean checkEntryMissingRDNValues() { return checkEntryMissingRDNValues; } /** * Specifies whether the entry validator should consider entries invalid if * they contain one or more attribute values in their RDN that are not present * in the set of entry attributes. * * @param checkEntryMissingRDNValues Indicates whether the entry validator * should consider entries invalid if they * contain one or more attribute values in * their RDN that are not present in the * set of entry attributes. */ public void setCheckEntryMissingRDNValues( final boolean checkEntryMissingRDNValues) { this.checkEntryMissingRDNValues = checkEntryMissingRDNValues; } /** * Indicates whether the entry validator should consider entries invalid if * the attributes contained in the RDN violate the constraints of the * associated name form. * * @return {@code true} if entries with RDNs that do not conform to the * associated name form should be considered invalid, or * {@code false} if not. */ public boolean checkNameForms() { return checkNameForms; } /** * Specifies whether the entry validator should consider entries invalid if * the attributes contained in the RDN violate the constraints of the * associated name form. * * @param checkNameForms Indicates whether the entry validator should * consider entries invalid if their RDNs violate name * form constraints. */ public void setCheckNameForms(final boolean checkNameForms) { this.checkNameForms = checkNameForms; } /** * Indicates whether the entry validator should consider entries invalid if * they contain attributes which are not allowed by (or are prohibited by) the * object classes and DIT content rule (if applicable) for the entry. * * @return {@code true} if entries should be considered invalid if they * contain attributes which are not allowed, or {@code false} if not. */ public boolean checkProhibitedAttributes() { return checkProhibitedAttributes; } /** * Specifies whether the entry validator should consider entries invalid if * they contain attributes which are not allowed by (or are prohibited by) the * object classes and DIT content rule (if applicable) for the entry. * * @param checkProhibitedAttributes Indicates whether entries should be * considered invalid if they contain * attributes which are not allowed. */ public void setCheckProhibitedAttributes( final boolean checkProhibitedAttributes) { this.checkProhibitedAttributes = checkProhibitedAttributes; } /** * Indicates whether the entry validator should consider entries invalid if * they contain auxiliary object classes which are not allowed by the DIT * content rule (if applicable) for the entry, or if they contain any abstract * object classes which are not subclassed by any non-abstract classes * included in the entry. * * @return {@code true} if entries should be considered invalid if they * contain prohibited object classes, or {@code false} if not. */ public boolean checkProhibitedObjectClasses() { return checkProhibitedObjectClasses; } /** * Specifies whether the entry validator should consider entries invalid if * they contain auxiliary object classes which are not allowed by the DIT * content rule (if applicable) for the entry, or if they contain any abstract * object classes which are not subclassed by any non-abstract classes * included in the entry. * * @param checkProhibitedObjectClasses Indicates whether entries should be * considered invalid if they contain * prohibited object classes. */ public void setCheckProhibitedObjectClasses( final boolean checkProhibitedObjectClasses) { this.checkProhibitedObjectClasses = checkProhibitedObjectClasses; } /** * Indicates whether the entry validator should consider entries invalid if * they they contain attributes with more than one value which are declared as * single-valued in the schema. * * @return {@code true} if entries should be considered invalid if they * contain single-valued attributes with more than one value, or * {@code false} if not. */ public boolean checkSingleValuedAttributes() { return checkSingleValuedAttributes; } /** * Specifies whether the entry validator should consider entries invalid if * they contain attributes with more than one value which are declared as * single-valued in the schema. * * @param checkSingleValuedAttributes Indicates whether entries should be * considered invalid if they contain * single-valued attributes with more * than one value. */ public void setCheckSingleValuedAttributes( final boolean checkSingleValuedAttributes) { this.checkSingleValuedAttributes = checkSingleValuedAttributes; } /** * Indicates whether the entry validator should consider entries invalid if * they do not contain exactly one structural object class (i.e., either do * not have any structural object class, or have more than one). * * @return {@code true} if entries should be considered invalid if they do * not have exactly one structural object class, or {@code false} if * not. */ public boolean checkStructuralObjectClasses() { return checkStructuralObjectClasses; } /** * Specifies whether the entry validator should consider entries invalid if * they do not contain exactly one structural object class (i.e., either do * not have any structural object class, or have more than one). * * @param checkStructuralObjectClasses Indicates whether entries should be * considered invalid if they do not * have exactly one structural object * class. */ public void setCheckStructuralObjectClasses( final boolean checkStructuralObjectClasses) { this.checkStructuralObjectClasses = checkStructuralObjectClasses; } /** * Indicates whether the entry validator should consider entries invalid if * they contain attributes which violate the associated attribute syntax. * * @return {@code true} if entries should be considered invalid if they * contain attribute values which violate the associated attribute * syntax, or {@code false} if not. */ public boolean checkAttributeSyntax() { return checkAttributeSyntax; } /** * Specifies whether the entry validator should consider entries invalid if * they contain attributes which violate the associated attribute syntax. * * @param checkAttributeSyntax Indicates whether entries should be * considered invalid if they violate the * associated attribute syntax. */ public void setCheckAttributeSyntax(final boolean checkAttributeSyntax) { this.checkAttributeSyntax = checkAttributeSyntax; } /** * Retrieves the set of attribute types for which syntax violations should be * ignored. If {@link #checkAttributeSyntax()} returns {@code true}, then * any attribute syntax violations will be flagged for all attributes except * those attributes in this set. If {@code checkAttributeSyntax()} returns * {@code false}, then all syntax violations will be ignored. * * @return The set of attribute types for which syntax violations should be * ignored. */ @NotNull() public Set getIgnoreSyntaxViolationsAttributeTypes() { return ignoreSyntaxViolationTypes; } /** * Specifies the set of attribute types for which syntax violations should be * ignored. This method will only have any effect if * {@link #checkAttributeSyntax()} returns {@code true}. * * @param attributeTypes The definitions for the attribute types for which * to ignore syntax violations. It may be * {@code null} or empty if no violations should be * ignored. */ public void setIgnoreSyntaxViolationAttributeTypes( @Nullable final AttributeTypeDefinition... attributeTypes) { if (attributeTypes == null) { ignoreSyntaxViolationTypes = Collections.emptySet(); } else { ignoreSyntaxViolationTypes = Collections.unmodifiableSet( new HashSet<>(StaticUtils.toList(attributeTypes))); } } /** * Specifies the names or OIDs of the attribute types for which syntax * violations should be ignored. This method will only have any effect if * {@link #checkAttributeSyntax()} returns {@code true}. * * @param attributeTypes The names or OIDs of the attribute types for which * to ignore syntax violations. It may be * {@code null} or empty if no violations should be * ignored. */ public void setIgnoreSyntaxViolationAttributeTypes( @Nullable final String... attributeTypes) { setIgnoreSyntaxViolationAttributeTypes(StaticUtils.toList(attributeTypes)); } /** * Specifies the names or OIDs of the attribute types for which syntax * violations should be ignored. This method will only have any effect if * {@link #checkAttributeSyntax()} returns {@code true}. * * @param attributeTypes The names or OIDs of the attribute types for which * to ignore syntax violations. It may be * {@code null} or empty if no violations should be * ignored. Any attribute types not defined in the * schema will be ignored. */ public void setIgnoreSyntaxViolationAttributeTypes( @Nullable final Collection attributeTypes) { if (attributeTypes == null) { ignoreSyntaxViolationTypes = Collections.emptySet(); return; } final HashSet atSet = new HashSet<>(StaticUtils.computeMapCapacity(attributeTypes.size())); for (final String s : attributeTypes) { final AttributeTypeDefinition d = schema.getAttributeType(s); if (d != null) { atSet.add(d); } } ignoreSyntaxViolationTypes = Collections.unmodifiableSet(atSet); } /** * Indicates whether the entry validator should consider entries invalid if * they contain attributes which are not defined in the schema. * * @return {@code true} if entries should be considered invalid if they * contain attributes which are not defined in the schema, or * {@code false} if not. */ public boolean checkUndefinedAttributes() { return checkUndefinedAttributes; } /** * Specifies whether the entry validator should consider entries invalid if * they contain attributes which are not defined in the schema. * * @param checkUndefinedAttributes Indicates whether entries should be * considered invalid if they contain * attributes which are not defined in the * schema, or {@code false} if not. */ public void setCheckUndefinedAttributes( final boolean checkUndefinedAttributes) { this.checkUndefinedAttributes = checkUndefinedAttributes; } /** * Indicates whether the entry validator should consider entries invalid if * they contain object classes which are not defined in the schema. * * @return {@code true} if entries should be considered invalid if they * contain object classes which are not defined in the schema, or * {@code false} if not. */ public boolean checkUndefinedObjectClasses() { return checkUndefinedObjectClasses; } /** * Specifies whether the entry validator should consider entries invalid if * they contain object classes which are not defined in the schema. * * @param checkUndefinedObjectClasses Indicates whether entries should be * considered invalid if they contain * object classes which are not defined * in the schema. */ public void setCheckUndefinedObjectClasses( final boolean checkUndefinedObjectClasses) { this.checkUndefinedObjectClasses = checkUndefinedObjectClasses; } /** * Indicates whether the provided entry passes all of the enabled types of * validation. * * @param entry The entry to be examined. It must not be * {@code null}. * @param invalidReasons A list to which messages may be added which provide * information about why the entry is invalid. It may * be {@code null} if this information is not needed. * * @return {@code true} if the entry conforms to all of the enabled forms of * validation, or {@code false} if the entry fails at least one of * the tests. */ public boolean entryIsValid(@NotNull final Entry entry, @Nullable final List invalidReasons) { Validator.ensureNotNull(entry); boolean entryValid = true; entriesExamined.incrementAndGet(); // Get the parsed DN for the entry. RDN rdn = null; try { rdn = entry.getParsedDN().getRDN(); } catch (final LDAPException le) { Debug.debugException(le); if (checkMalformedDNs) { entryValid = false; malformedDNs.incrementAndGet(); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_MALFORMED_DN.get( StaticUtils.getExceptionMessage(le))); } } } // Get the object class descriptions for the object classes in the entry. final HashSet ocSet = new HashSet<>(StaticUtils.computeMapCapacity(10)); final boolean missingOC = (! getObjectClasses(entry, ocSet, invalidReasons)); if (missingOC) { entryValid = false; } // If the entry was not missing any object classes, then get the structural // class for the entry and use it to get the associated DIT content rule and // name form. DITContentRuleDefinition ditContentRule = null; NameFormDefinition nameForm = null; if (! missingOC) { final AtomicReference ref = new AtomicReference<>(null); entryValid &= getStructuralClass(ocSet, ref, invalidReasons); final ObjectClassDefinition structuralClass = ref.get(); if (structuralClass != null) { ditContentRule = schema.getDITContentRule(structuralClass.getOID()); nameForm = schema.getNameFormByObjectClass(structuralClass.getNameOrOID()); } } // If we should check for missing required attributes, then do so. Set requiredAttrs = Collections.emptySet(); if (checkMissingAttributes || checkProhibitedAttributes) { requiredAttrs = getRequiredAttributes(ocSet, ditContentRule); if (checkMissingAttributes) { entryValid &= checkForMissingAttributes(entry, rdn, requiredAttrs, invalidReasons); } } // Iterate through all of the attributes in the entry. Make sure that they // are all defined in the schema, that they are allowed to be present in the // entry, that their values conform to the associated syntax, and that any // single-valued attributes have only one value. Set optionalAttrs = Collections.emptySet(); if (checkProhibitedAttributes) { optionalAttrs = getOptionalAttributes(ocSet, ditContentRule, requiredAttrs); } for (final Attribute a : entry.getAttributes()) { entryValid &= checkAttribute(a, requiredAttrs, optionalAttrs, invalidReasons); } // If there is a DIT content rule, then check to ensure that all of the // auxiliary object classes are allowed. if (checkProhibitedObjectClasses && (ditContentRule != null)) { entryValid &= checkAuxiliaryClasses(ocSet, ditContentRule, invalidReasons); } // Check the entry's RDN to ensure that all attributes are defined in the // schema, allowed to be present, and comply with the name form. if (rdn != null) { entryValid &= checkRDN(rdn, entry, requiredAttrs, optionalAttrs, nameForm, invalidReasons); } if (! entryValid) { invalidEntries.incrementAndGet(); } return entryValid; } /** * Gets the object classes for the entry, including any that weren't * explicitly included but should be because they were superior to classes * that were included. * * @param entry The entry to examine. * @param ocSet The set into which the object class definitions * should be placed. * @param invalidReasons A list to which messages may be added which provide * information about why the entry is invalid. It may * be {@code null} if this information is not needed. * * @return {@code true} if the entry passed all validation processing * performed by this method, or {@code false} if there were any * failures. */ private boolean getObjectClasses(@NotNull final Entry entry, @NotNull final HashSet ocSet, @Nullable final List invalidReasons) { final String[] ocValues = entry.getObjectClassValues(); if ((ocValues == null) || (ocValues.length == 0)) { noObjectClasses.incrementAndGet(); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_NO_OCS.get()); } return false; } boolean entryValid = true; final HashSet missingOCs = new HashSet<>(StaticUtils.computeMapCapacity(ocValues.length)); for (final String ocName : entry.getObjectClassValues()) { final ObjectClassDefinition d = schema.getObjectClass(ocName); if (d == null) { if (checkUndefinedObjectClasses) { entryValid = false; missingOCs.add(StaticUtils.toLowerCase(ocName)); updateCount(ocName, undefinedObjectClasses); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_UNDEFINED_OC.get(ocName)); } } } else { ocSet.add(d); } } for (final ObjectClassDefinition d : new HashSet<>(ocSet)) { entryValid &= addSuperiorClasses(d, ocSet, missingOCs, invalidReasons); } return entryValid; } /** * Recursively adds the definition superior class for the provided object * class definition to the provided set, if it is not already present. * * @param d The object class definition to process. * @param ocSet The set into which the object class definitions * should be placed. * @param missingOCNames The names of the object classes we already know are * missing and therefore shouldn't be flagged again. * @param invalidReasons A list to which messages may be added which provide * information about why the entry is invalid. It may * be {@code null} if this information is not needed. * * @return {@code true} if the entry passed all validation processing * performed by this method, or {@code false} if there were any * failures. */ private boolean addSuperiorClasses(@NotNull final ObjectClassDefinition d, @NotNull final HashSet ocSet, @NotNull final HashSet missingOCNames, @Nullable final List invalidReasons) { boolean entryValid = true; for (final String ocName : d.getSuperiorClasses()) { final ObjectClassDefinition supOC = schema.getObjectClass(ocName); if (supOC == null) { if (checkUndefinedObjectClasses) { entryValid = false; final String lowerName = StaticUtils.toLowerCase(ocName); if (! missingOCNames.contains(lowerName)) { missingOCNames.add(lowerName); updateCount(ocName, undefinedObjectClasses); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_UNDEFINED_SUP_OC.get( d.getNameOrOID(), ocName)); } } } } else { if (! ocSet.contains(supOC)) { ocSet.add(supOC); if (checkMissingSuperiorObjectClasses) { entryValid = false; missingSuperiorClasses.incrementAndGet(); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_MISSING_SUP_OC.get( supOC.getNameOrOID(), d.getNameOrOID())); } } } entryValid &= addSuperiorClasses(supOC, ocSet, missingOCNames, invalidReasons); } } return entryValid; } /** * Retrieves the structural object class from the set of provided object * classes. * * @param ocSet The set of object class definitions for the entry. * @param structuralClass The reference that will be updated with the * entry's structural object class. * @param invalidReasons A list to which messages may be added which * provide provide information about why the entry is * invalid. It may be {@code null} if this * information is not needed. * * @return {@code true} if the entry passes all validation checks performed * by this method, or {@code false} if not. */ private boolean getStructuralClass( @NotNull final HashSet ocSet, @NotNull final AtomicReference structuralClass, @Nullable final List invalidReasons) { final HashSet ocCopy = new HashSet<>(ocSet); for (final ObjectClassDefinition d : ocSet) { final ObjectClassType t = d.getObjectClassType(schema); if (t == ObjectClassType.STRUCTURAL) { ocCopy.removeAll(d.getSuperiorClasses(schema, true)); } else if (t == ObjectClassType.AUXILIARY) { ocCopy.remove(d); ocCopy.removeAll(d.getSuperiorClasses(schema, true)); } } // Iterate through the set of remaining classes and strip out any // abstract classes. boolean entryValid = true; Iterator iterator = ocCopy.iterator(); while (iterator.hasNext()) { final ObjectClassDefinition d = iterator.next(); if (d.getObjectClassType(schema) == ObjectClassType.ABSTRACT) { if (checkProhibitedObjectClasses) { entryValid = false; updateCount(d.getNameOrOID(), prohibitedObjectClasses); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_INVALID_ABSTRACT_CLASS.get( d.getNameOrOID())); } } iterator.remove(); } } switch (ocCopy.size()) { case 0: if (checkStructuralObjectClasses) { entryValid = false; noStructuralClass.incrementAndGet(); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_NO_STRUCTURAL_CLASS.get()); } } break; case 1: structuralClass.set(ocCopy.iterator().next()); break; default: if (checkStructuralObjectClasses) { entryValid = false; multipleStructuralClasses.incrementAndGet(); if (invalidReasons != null) { final StringBuilder ocList = new StringBuilder(); iterator = ocCopy.iterator(); while (iterator.hasNext()) { ocList.append(iterator.next().getNameOrOID()); if (iterator.hasNext()) { ocList.append(", "); } } invalidReasons.add( ERR_ENTRY_MULTIPLE_STRUCTURAL_CLASSES.get(ocList)); } } break; } return entryValid; } /** * Retrieves the set of attributes which must be present in entries with the * provided set of object classes and DIT content rule. * * @param ocSet The set of object classes for the entry. * @param ditContentRule The DIT content rule for the entry, if defined. * * @return The set of attributes which must be present in entries with the * provided set of object classes and DIT content rule. */ @NotNull() private HashSet getRequiredAttributes( @NotNull final HashSet ocSet, @Nullable final DITContentRuleDefinition ditContentRule) { final HashSet attrSet = new HashSet<>(StaticUtils.computeMapCapacity(20)); for (final ObjectClassDefinition oc : ocSet) { attrSet.addAll(oc.getRequiredAttributes(schema, false)); } if (ditContentRule != null) { for (final String s : ditContentRule.getRequiredAttributes()) { final AttributeTypeDefinition d = schema.getAttributeType(s); if (d != null) { attrSet.add(d); } } } return attrSet; } /** * Retrieves the set of attributes which may optionally be present in entries * with the provided set of object classes and DIT content rule. * * @param ocSet The set of object classes for the entry. * @param ditContentRule The DIT content rule for the entry, if defined. * @param requiredAttrSet The set of required attributes for the entry. * * @return The set of attributes which may optionally be present in entries * with the provided set of object classes and DIT content rule. */ @NotNull() private HashSet getOptionalAttributes( @NotNull final HashSet ocSet, @Nullable final DITContentRuleDefinition ditContentRule, @NotNull final Set requiredAttrSet) { final HashSet attrSet = new HashSet<>(StaticUtils.computeMapCapacity(20)); for (final ObjectClassDefinition oc : ocSet) { if (oc.hasNameOrOID("extensibleObject") || oc.hasNameOrOID("1.3.6.1.4.1.1466.101.120.111")) { attrSet.addAll(schema.getUserAttributeTypes()); break; } for (final AttributeTypeDefinition d : oc.getOptionalAttributes(schema, false)) { if (! requiredAttrSet.contains(d)) { attrSet.add(d); } } } if (ditContentRule != null) { for (final String s : ditContentRule.getOptionalAttributes()) { final AttributeTypeDefinition d = schema.getAttributeType(s); if ((d != null) && (! requiredAttrSet.contains(d))) { attrSet.add(d); } } for (final String s : ditContentRule.getProhibitedAttributes()) { final AttributeTypeDefinition d = schema.getAttributeType(s); if (d != null) { attrSet.remove(d); } } } return attrSet; } /** * Checks the provided entry to determine whether it is missing any required * attributes. * * @param entry The entry to examine. * @param rdn The RDN for the entry, if available. * @param requiredAttrs The set of attribute types which are required to be * included in the entry. * @param invalidReasons A list to which messages may be added which provide * information about why the entry is invalid. It may * be {@code null} if this information is not needed. * * @return {@code true} if the entry has all required attributes, or * {@code false} if not. */ private boolean checkForMissingAttributes(@NotNull final Entry entry, @Nullable final RDN rdn, @NotNull final Set requiredAttrs, @Nullable final List invalidReasons) { boolean entryValid = true; for (final AttributeTypeDefinition d : requiredAttrs) { boolean found = false; for (final String s : d.getNames()) { if (entry.hasAttribute(s) || ((rdn != null) && rdn.hasAttribute(s))) { found = true; break; } } if (! found) { if (! (entry.hasAttribute(d.getOID()) || ((rdn != null) && (rdn.hasAttribute(d.getOID()))))) { entryValid = false; updateCount(d.getNameOrOID(), missingAttributes); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_MISSING_REQUIRED_ATTR.get( d.getNameOrOID())); } } } } return entryValid; } /** * Checks the provided attribute to determine whether it appears to be valid. * * @param attr The attribute to examine. * @param requiredAttrs The set of attribute types which are required to be * included in the entry. * @param optionalAttrs The set of attribute types which may optionally be * included in the entry. * @param invalidReasons A list to which messages may be added which provide * information about why the entry is invalid. It may * be {@code null} if this information is not needed. * * @return {@code true} if the attribute passed all of the checks and appears * to be valid, or {@code false} if it failed any of the checks. */ private boolean checkAttribute(@NotNull final Attribute attr, @NotNull final Set requiredAttrs, @NotNull final Set optionalAttrs, @Nullable final List invalidReasons) { boolean entryValid = true; final AttributeTypeDefinition d = schema.getAttributeType(attr.getBaseName()); if (d == null) { if (checkUndefinedAttributes) { entryValid = false; updateCount(attr.getBaseName(), undefinedAttributes); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_UNDEFINED_ATTR.get(attr.getBaseName())); } } return entryValid; } if (checkProhibitedAttributes && (! d.isOperational())) { if (! (requiredAttrs.contains(d) || optionalAttrs.contains(d))) { entryValid = false; updateCount(d.getNameOrOID(), prohibitedAttributes); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_ATTR_NOT_ALLOWED.get(d.getNameOrOID())); } } } final ASN1OctetString[] rawValues = attr.getRawValues(); if (checkSingleValuedAttributes && d.isSingleValued() && (rawValues.length > 1)) { entryValid = false; updateCount(d.getNameOrOID(), singleValueViolations); if (invalidReasons != null) { invalidReasons.add( ERR_ENTRY_ATTR_HAS_MULTIPLE_VALUES.get(d.getNameOrOID())); } } if (checkAttributeSyntax) { if (! ignoreSyntaxViolationTypes.contains(d)) { final MatchingRule r = MatchingRule.selectEqualityMatchingRule(d.getNameOrOID(), schema); final Map extensions = d.getExtensions(); for (final ASN1OctetString v : rawValues) { try { r.normalize(v); } catch (final LDAPException le) { Debug.debugException(le); entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_ATTR_INVALID_SYNTAX.get( v.stringValue(), d.getNameOrOID(), StaticUtils.getExceptionMessage(le))); } } // If the attribute type definition includes an X-ALLOWED-VALUE // extension, then make sure the value is in that set. final String[] allowedValues = extensions.get("X-ALLOWED-VALUE"); if (allowedValues != null) { boolean isAllowed = false; for (final String allowedValue : allowedValues) { try { if (r.valuesMatch(v, new ASN1OctetString(allowedValue))) { isAllowed = true; break; } } catch (final Exception e) { Debug.debugException(e); } } if (! isAllowed) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_ALLOWED.get( v.stringValue(), d.getNameOrOID())); } } } // If the attribute type definition includes an X-VALUE-REGEX // extension, then make sure the value matches one of those regexes. final String[] valueRegexes = extensions.get("X-VALUE-REGEX"); if (valueRegexes != null) { boolean matchesRegex = false; for (final String regex : valueRegexes) { try { final Pattern pattern = Pattern.compile(regex); if (pattern.matcher(v.stringValue()).matches()) { matchesRegex = true; break; } } catch (final Exception e) { Debug.debugException(e); } } if (! matchesRegex) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add( ERR_ENTRY_ATTR_VALUE_NOT_ALLOWED_BY_REGEX.get( v.stringValue(), d.getNameOrOID())); } } } // If the attribute type definition includes an X-MIN-VALUE-LENGTH // extension, then make sure the value is long enough. final String[] minValueLengths = extensions.get("X-MIN-VALUE-LENGTH"); if (minValueLengths != null) { int minLength = 0; for (final String s : minValueLengths) { try { minLength = Math.max(minLength, Integer.parseInt(s)); } catch (final Exception e) { Debug.debugException(e); } } if (v.stringValue().length() < minLength) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add( ERR_ENTRY_ATTR_VALUE_SHORTER_THAN_MIN_LENGTH.get( v.stringValue(), d.getNameOrOID(), minLength)); } } } // If the attribute type definition includes an X-MAX-VALUE-LENGTH // extension, then make sure the value is short enough. final String[] maxValueLengths = extensions.get("X-MAX-VALUE-LENGTH"); if (maxValueLengths != null) { int maxLength = Integer.MAX_VALUE; for (final String s : maxValueLengths) { try { maxLength = Math.min(maxLength, Integer.parseInt(s)); } catch (final Exception e) { Debug.debugException(e); } } if (v.stringValue().length() > maxLength) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add( ERR_ENTRY_ATTR_VALUE_LONGER_THAN_MAX_LENGTH.get( v.stringValue(), d.getNameOrOID(), maxLength)); } } } // If the attribute type definition includes an X-MIN-INT-VALUE // extension, then make sure the value is large enough. final String[] minIntValues = extensions.get("X-MIN-INT-VALUE"); if (minIntValues != null) { try { final long longValue = Long.parseLong(v.stringValue()); long minAllowedValue = 0L; for (final String s : minIntValues) { try { minAllowedValue = Math.max(minAllowedValue, Long.parseLong(s)); } catch (final Exception e) { Debug.debugException(e); } } if (longValue < minAllowedValue) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_ATTR_VALUE_INT_TOO_SMALL.get( longValue, d.getNameOrOID(), minAllowedValue)); } } } catch (final Exception e) { Debug.debugException(e); entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_INT.get( v.stringValue(), d.getNameOrOID(), "X-MIN-INT-VALUE")); } } } // If the attribute type definition includes an X-MAX-INT-VALUE // extension, then make sure the value is large enough. final String[] maxIntValues = extensions.get("X-MAX-INT-VALUE"); if (maxIntValues != null) { try { final long longValue = Long.parseLong(v.stringValue()); long maxAllowedValue = Long.MAX_VALUE; for (final String s : maxIntValues) { try { maxAllowedValue = Math.min(maxAllowedValue, Long.parseLong(s)); } catch (final Exception e) { Debug.debugException(e); } } if (longValue > maxAllowedValue) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_ATTR_VALUE_INT_TOO_LARGE.get( longValue, d.getNameOrOID(), maxAllowedValue)); } } } catch (final Exception e) { Debug.debugException(e); entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_INT.get( v.stringValue(), d.getNameOrOID(), "X-MAX-INT-VALUE")); } } } } // If the attribute type definition includes an X-MIN-VALUE-COUNT // extension, then make sure the value has enough values. final String[] minValueCounts = extensions.get("X-MIN-VALUE-COUNT"); if (minValueCounts != null) { int minValueCount = 0; for (final String s : minValueCounts) { try { minValueCount = Math.max(minValueCount, Integer.parseInt(s)); } catch (final Exception e) { Debug.debugException(e); } } if (rawValues.length < minValueCount) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_TOO_FEW_VALUES.get(rawValues.length, d.getNameOrOID(), minValueCount)); } } } // If the attribute type definition includes an X-MAX-VALUE-COUNT // extension, then make sure the value has enough values. final String[] maxValueCounts = extensions.get("X-MAX-VALUE-COUNT"); if (maxValueCounts != null) { int maxValueCount = Integer.MAX_VALUE; for (final String s : maxValueCounts) { try { maxValueCount = Math.min(maxValueCount, Integer.parseInt(s)); } catch (final Exception e) { Debug.debugException(e); } } if (rawValues.length > maxValueCount) { entryValid = false; updateCount(d.getNameOrOID(), attributesViolatingSyntax); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_TOO_MANY_VALUES.get(rawValues.length, d.getNameOrOID(), maxValueCount)); } } } } } return entryValid; } /** * Ensures that all of the auxiliary object classes contained in the object * class set are allowed by the provided DIT content rule. * * @param ocSet The set of object classes contained in the entry. * @param ditContentRule The DIT content rule to use to make the * determination. * @param invalidReasons A list to which messages may be added which provide * information about why the entry is invalid. It may * be {@code null} if this information is not needed. * * @return {@code true} if the entry passes all checks performed by this * method, or {@code false} if not. */ private boolean checkAuxiliaryClasses( @NotNull final HashSet ocSet, @NotNull final DITContentRuleDefinition ditContentRule, @Nullable final List invalidReasons) { final HashSet auxSet = new HashSet<>(StaticUtils.computeMapCapacity(20)); for (final String s : ditContentRule.getAuxiliaryClasses()) { final ObjectClassDefinition d = schema.getObjectClass(s); if (d != null) { auxSet.add(d); } } boolean entryValid = true; for (final ObjectClassDefinition d : ocSet) { final ObjectClassType t = d.getObjectClassType(schema); if ((t == ObjectClassType.AUXILIARY) && (! auxSet.contains(d))) { entryValid = false; updateCount(d.getNameOrOID(), prohibitedObjectClasses); if (invalidReasons != null) { invalidReasons.add( ERR_ENTRY_AUX_CLASS_NOT_ALLOWED.get(d.getNameOrOID())); } } } return entryValid; } /** * Ensures that the provided RDN is acceptable. It will ensure that all * attributes are defined in the schema and allowed for the entry, and that * the entry optionally conforms to the associated name form. * * @param rdn The RDN to examine. * @param entry The entry to examine. * @param requiredAttrs The set of attribute types which are required to be * included in the entry. * @param optionalAttrs The set of attribute types which may optionally be * included in the entry. * @param nameForm The name for to use to make the determination, if * defined. * @param invalidReasons A list to which messages may be added which provide * information about why the entry is invalid. It may * be {@code null} if this information is not needed. * * @return {@code true} if the entry passes all checks performed by this * method, or {@code false} if not. */ private boolean checkRDN(@NotNull final RDN rdn, @NotNull final Entry entry, @NotNull final Set requiredAttrs, @NotNull final Set optionalAttrs, @Nullable final NameFormDefinition nameForm, @Nullable final List invalidReasons) { final HashSet nfReqAttrs = new HashSet<>(StaticUtils.computeMapCapacity(5)); final HashSet nfAllowedAttrs = new HashSet<>(StaticUtils.computeMapCapacity(5)); if (nameForm != null) { for (final String s : nameForm.getRequiredAttributes()) { final AttributeTypeDefinition d = schema.getAttributeType(s); if (d != null) { nfReqAttrs.add(d); } } nfAllowedAttrs.addAll(nfReqAttrs); for (final String s : nameForm.getOptionalAttributes()) { final AttributeTypeDefinition d = schema.getAttributeType(s); if (d != null) { nfAllowedAttrs.add(d); } } } boolean entryValid = true; final String[] attributeNames = rdn.getAttributeNames(); final byte[][] attributeValues = rdn.getByteArrayAttributeValues(); for (int i=0; i < attributeNames.length; i++) { final String name = attributeNames[i]; if (checkEntryMissingRDNValues) { final byte[] value = attributeValues[i]; final MatchingRule matchingRule = MatchingRule.selectEqualityMatchingRule(name, schema); if (! entry.hasAttributeValue(name, value, matchingRule)) { entryValid = false; entriesMissingRDNValues.incrementAndGet(); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_MISSING_RDN_VALUE.get( rdn.getAttributeValues()[i], name)); } } } final AttributeTypeDefinition d = schema.getAttributeType(name); if (d == null) { if (checkUndefinedAttributes) { entryValid = false; updateCount(name, undefinedAttributes); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_RDN_ATTR_NOT_DEFINED.get(name)); } } } else { if (checkProhibitedAttributes && (! (requiredAttrs.contains(d) || optionalAttrs.contains(d) || d.isOperational()))) { entryValid = false; updateCount(d.getNameOrOID(), prohibitedAttributes); if (invalidReasons != null) { invalidReasons.add(ERR_ENTRY_RDN_ATTR_NOT_ALLOWED_IN_ENTRY.get( d.getNameOrOID())); } } if (checkNameForms && (nameForm != null)) { if (! nfReqAttrs.remove(d)) { if (! nfAllowedAttrs.contains(d)) { if (entryValid) { entryValid = false; nameFormViolations.incrementAndGet(); } if (invalidReasons != null) { invalidReasons.add( ERR_ENTRY_RDN_ATTR_NOT_ALLOWED_BY_NF.get(name)); } } } } } } if (checkNameForms && (! nfReqAttrs.isEmpty())) { if (entryValid) { entryValid = false; nameFormViolations.incrementAndGet(); } if (invalidReasons != null) { for (final AttributeTypeDefinition d : nfReqAttrs) { invalidReasons.add(ERR_ENTRY_RDN_MISSING_REQUIRED_ATTR.get( d.getNameOrOID())); } } } return entryValid; } /** * Updates the count for the given key in the provided map, adding a new key * with a count of one if necessary. * * @param key The key for which the count is to be updated. * @param map The map in which the update is to be made. */ private static void updateCount(@NotNull final String key, @NotNull final ConcurrentHashMap map) { final String lowerKey = StaticUtils.toLowerCase(key); AtomicLong l = map.get(lowerKey); if (l == null) { l = map.putIfAbsent(lowerKey, new AtomicLong(1L)); if (l == null) { return; } } l.incrementAndGet(); } /** * Resets all counts maintained by this entry validator. */ public void resetCounts() { entriesExamined.set(0L); entriesMissingRDNValues.set(0L); invalidEntries.set(0L); malformedDNs.set(0L); missingSuperiorClasses.set(0L); multipleStructuralClasses.set(0L); nameFormViolations.set(0L); noObjectClasses.set(0L); noStructuralClass.set(0L); attributesViolatingSyntax.clear(); missingAttributes.clear(); prohibitedAttributes.clear(); prohibitedObjectClasses.clear(); singleValueViolations.clear(); undefinedAttributes.clear(); undefinedObjectClasses.clear(); } /** * Retrieves the total number of entries examined during processing. * * @return The total number of entries examined during processing. */ public long getEntriesExamined() { return entriesExamined.get(); } /** * Retrieves the total number of invalid entries encountered during * processing. * * @return The total number of invalid entries encountered during processing. */ public long getInvalidEntries() { return invalidEntries.get(); } /** * Retrieves the total number of entries examined that had malformed DNs which * could not be parsed. * * @return The total number of entries examined that had malformed DNs. */ public long getMalformedDNs() { return malformedDNs.get(); } /** * Retrieves the total number of entries examined that included an attribute * value in the RDN that was not present in the entry attributes. * * @return The total number of entries examined that included an attribute * value in the RDN that was not present in the entry attributes. */ public long getEntriesMissingRDNValues() { return entriesMissingRDNValues.get(); } /** * Retrieves the total number of entries examined which did not contain any * object classes. * * @return The total number of entries examined which did not contain any * object classes. */ public long getEntriesWithoutAnyObjectClasses() { return noObjectClasses.get(); } /** * Retrieves the total number of entries examined which did not contain any * structural object class. * * @return The total number of entries examined which did not contain any * structural object class. */ public long getEntriesMissingStructuralObjectClass() { return noStructuralClass.get(); } /** * Retrieves the total number of entries examined which contained more than * one structural object class. * * @return The total number of entries examined which contained more than one * structural object class. */ public long getEntriesWithMultipleStructuralObjectClasses() { return multipleStructuralClasses.get(); } /** * Retrieves the total number of entries examined which were missing one or * more superior object classes. * * @return The total number of entries examined which were missing one or * more superior object classes. */ public long getEntriesWithMissingSuperiorObjectClasses() { return missingSuperiorClasses.get(); } /** * Retrieves the total number of entries examined which contained an RDN that * violated the constraints of the associated name form. * * @return The total number of entries examined which contained an RDN that * violated the constraints of the associated name form. */ public long getNameFormViolations() { return nameFormViolations.get(); } /** * Retrieves the total number of undefined object classes encountered while * examining entries. Note that this number may be greater than the total * number of entries examined if entries contain multiple undefined object * classes. * * @return The total number of undefined object classes encountered while * examining entries. */ public long getTotalUndefinedObjectClasses() { return getMapTotal(undefinedObjectClasses); } /** * Retrieves the undefined object classes encountered while processing * entries, mapped from the name of the undefined object class to the number * of entries in which that object class was referenced. * * @return The undefined object classes encountered while processing entries. */ @NotNull() public Map getUndefinedObjectClasses() { return convertMap(undefinedObjectClasses); } /** * Retrieves the total number of undefined attribute types encountered while * examining entries. Note that this number may be greater than the total * number of entries examined if entries contain multiple undefined attribute * types. * * @return The total number of undefined attribute types encountered while * examining entries. */ public long getTotalUndefinedAttributes() { return getMapTotal(undefinedAttributes); } /** * Retrieves the undefined attribute types encountered while processing * entries, mapped from the name of the undefined attribute to the number * of entries in which that attribute type was referenced. * * @return The undefined attribute types encountered while processing * entries. */ @NotNull() public Map getUndefinedAttributes() { return convertMap(undefinedAttributes); } /** * Retrieves the total number of prohibited object classes encountered while * examining entries. Note that this number may be greater than the total * number of entries examined if entries contain multiple prohibited object * classes. * * @return The total number of prohibited object classes encountered while * examining entries. */ public long getTotalProhibitedObjectClasses() { return getMapTotal(prohibitedObjectClasses); } /** * Retrieves the prohibited object classes encountered while processing * entries, mapped from the name of the object class to the number of entries * in which that object class was referenced. * * @return The prohibited object classes encountered while processing * entries. */ @NotNull() public Map getProhibitedObjectClasses() { return convertMap(prohibitedObjectClasses); } /** * Retrieves the total number of prohibited attributes encountered while * examining entries. Note that this number may be greater than the total * number of entries examined if entries contain multiple prohibited * attributes. * * @return The total number of prohibited attributes encountered while * examining entries. */ public long getTotalProhibitedAttributes() { return getMapTotal(prohibitedAttributes); } /** * Retrieves the prohibited attributes encountered while processing entries, * mapped from the name of the attribute to the number of entries in which * that attribute was referenced. * * @return The prohibited attributes encountered while processing entries. */ @NotNull() public Map getProhibitedAttributes() { return convertMap(prohibitedAttributes); } /** * Retrieves the total number of missing required attributes encountered while * examining entries. Note that this number may be greater than the total * number of entries examined if entries are missing multiple attributes. * * @return The total number of missing required attributes encountered while * examining entries. */ public long getTotalMissingAttributes() { return getMapTotal(missingAttributes); } /** * Retrieves the missing required encountered while processing entries, mapped * from the name of the attribute to the number of entries in which that * attribute was required but not found. * * @return The prohibited attributes encountered while processing entries. */ @NotNull() public Map getMissingAttributes() { return convertMap(missingAttributes); } /** * Retrieves the total number of attribute values which violate their * associated syntax that were encountered while examining entries. Note that * this number may be greater than the total number of entries examined if * entries contain multiple malformed attribute values. * * @return The total number of attribute values which violate their * associated syntax that were encountered while examining entries. */ public long getTotalAttributesViolatingSyntax() { return getMapTotal(attributesViolatingSyntax); } /** * Retrieves the attributes with values violating their associated syntax that * were encountered while processing entries, mapped from the name of the * attribute to the number of malformed values found for that attribute. * * @return The attributes with malformed values encountered while processing * entries. */ @NotNull() public Map getAttributesViolatingSyntax() { return convertMap(attributesViolatingSyntax); } /** * Retrieves the total number of attributes defined as single-valued that * contained multiple values which were encountered while processing entries. * Note that this number may be greater than the total number of entries * examined if entries contain multiple such attributes. * * @return The total number of attribute defined as single-valued that * contained multiple values which were encountered while processing * entries. */ public long getTotalSingleValueViolations() { return getMapTotal(singleValueViolations); } /** * Retrieves the attributes defined as single-valued that contained multiple * values which were encountered while processing entries, mapped from the * name of the attribute to the number of entries in which that attribute had * multiple values. * * @return The attributes defined as single-valued that contained multiple * values which were encountered while processing entries. */ @NotNull() public Map getSingleValueViolations() { return convertMap(singleValueViolations); } /** * Retrieves the total number of occurrences for all items in the provided * map. * * @param map The map to be processed. * * @return The total number of occurrences for all items in the provided map. */ private static long getMapTotal(@NotNull final Map map) { long total = 0L; for (final AtomicLong l : map.values()) { total += l.longValue(); } return total; } /** * Converts the provided map from strings to atomic longs to a map from * strings to longs. * * @param map The map to be processed. * * @return The new map. */ @NotNull() private static Map convertMap( @NotNull final Map map) { final TreeMap m = new TreeMap<>(); for (final Map.Entry e : map.entrySet()) { m.put(e.getKey(), e.getValue().longValue()); } return Collections.unmodifiableMap(m); } /** * Retrieves a list of messages providing a summary of the invalid entries * processed by this class. * * @param detailedResults Indicates whether to include detailed information * about the attributes and object classes * responsible for the violations. * * @return A list of messages providing a summary of the invalid entries * processed by this class, or an empty list if all entries examined * were valid. */ @NotNull() public List getInvalidEntrySummary(final boolean detailedResults) { final long numInvalid = invalidEntries.get(); if (numInvalid == 0) { return Collections.emptyList(); } final ArrayList messages = new ArrayList<>(5); final long numEntries = entriesExamined.get(); long pct = 100 * numInvalid / numEntries; messages.add(INFO_ENTRY_INVALID_ENTRY_COUNT.get( numInvalid, numEntries, pct)); final long numBadDNs = malformedDNs.get(); if (numBadDNs > 0) { pct = 100 * numBadDNs / numEntries; messages.add(INFO_ENTRY_MALFORMED_DN_COUNT.get( numBadDNs, numEntries, pct)); } final long numEntriesMissingRDNValues = entriesMissingRDNValues.get(); if (numEntriesMissingRDNValues > 0) { pct = 100* numEntriesMissingRDNValues / numEntries; messages.add(INFO_ENTRY_MISSING_RDN_VALUE_COUNT.get( numEntriesMissingRDNValues, numEntries, pct)); } final long numNoOCs = noObjectClasses.get(); if (numNoOCs > 0) { pct = 100 * numNoOCs / numEntries; messages.add(INFO_ENTRY_NO_OC_COUNT.get(numNoOCs, numEntries, pct)); } final long numMissingStructural = noStructuralClass.get(); if (numMissingStructural > 0) { pct = 100 * numMissingStructural / numEntries; messages.add(INFO_ENTRY_NO_STRUCTURAL_OC_COUNT.get( numMissingStructural, numEntries, pct)); } final long numMultipleStructural = multipleStructuralClasses.get(); if (numMultipleStructural > 0) { pct = 100 * numMultipleStructural / numEntries; messages.add(INFO_ENTRY_MULTIPLE_STRUCTURAL_OCS_COUNT.get( numMultipleStructural, numEntries, pct)); } final long numNFViolations = nameFormViolations.get(); if (numNFViolations > 0) { pct = 100 * numNFViolations / numEntries; messages.add(INFO_ENTRY_NF_VIOLATION_COUNT.get( numNFViolations, numEntries, pct)); } final long numUndefinedOCs = getTotalUndefinedObjectClasses(); if (numUndefinedOCs > 0) { messages.add(INFO_ENTRY_UNDEFINED_OC_COUNT.get(numUndefinedOCs)); if (detailedResults) { for (final Map.Entry e : undefinedObjectClasses.entrySet()) { messages.add(INFO_ENTRY_UNDEFINED_OC_NAME_COUNT.get( e.getKey(), e.getValue().longValue())); } } } final long numProhibitedOCs = getTotalProhibitedObjectClasses(); if (numProhibitedOCs > 0) { messages.add(INFO_ENTRY_PROHIBITED_OC_COUNT.get(numProhibitedOCs)); if (detailedResults) { for (final Map.Entry e : prohibitedObjectClasses.entrySet()) { messages.add(INFO_ENTRY_PROHIBITED_OC_NAME_COUNT.get( e.getKey(), e.getValue().longValue())); } } } final long numMissingSuperior = getEntriesWithMissingSuperiorObjectClasses(); if (numMissingSuperior > 0) { messages.add( INFO_ENTRY_MISSING_SUPERIOR_OC_COUNT.get(numMissingSuperior)); } final long numUndefinedAttrs = getTotalUndefinedAttributes(); if (numUndefinedAttrs > 0) { messages.add(INFO_ENTRY_UNDEFINED_ATTR_COUNT.get(numUndefinedAttrs)); if (detailedResults) { for (final Map.Entry e : undefinedAttributes.entrySet()) { messages.add(INFO_ENTRY_UNDEFINED_ATTR_NAME_COUNT.get( e.getKey(), e.getValue().longValue())); } } } final long numMissingAttrs = getTotalMissingAttributes(); if (numMissingAttrs > 0) { messages.add(INFO_ENTRY_MISSING_ATTR_COUNT.get(numMissingAttrs)); if (detailedResults) { for (final Map.Entry e : missingAttributes.entrySet()) { messages.add(INFO_ENTRY_MISSING_ATTR_NAME_COUNT.get( e.getKey(), e.getValue().longValue())); } } } final long numProhibitedAttrs = getTotalProhibitedAttributes(); if (numProhibitedAttrs > 0) { messages.add(INFO_ENTRY_PROHIBITED_ATTR_COUNT.get(numProhibitedAttrs)); if (detailedResults) { for (final Map.Entry e : prohibitedAttributes.entrySet()) { messages.add(INFO_ENTRY_PROHIBITED_ATTR_NAME_COUNT.get( e.getKey(), e.getValue().longValue())); } } } final long numSingleValuedViolations = getTotalSingleValueViolations(); if (numSingleValuedViolations > 0) { messages.add(INFO_ENTRY_SINGLE_VALUE_VIOLATION_COUNT.get( numSingleValuedViolations)); if (detailedResults) { for (final Map.Entry e : singleValueViolations.entrySet()) { messages.add(INFO_ENTRY_SINGLE_VALUE_VIOLATION_NAME_COUNT.get( e.getKey(), e.getValue().longValue())); } } } final long numSyntaxViolations = getTotalAttributesViolatingSyntax(); if (numSyntaxViolations > 0) { messages.add(INFO_ENTRY_SYNTAX_VIOLATION_COUNT.get(numSyntaxViolations)); if (detailedResults) { for (final Map.Entry e : attributesViolatingSyntax.entrySet()) { messages.add(INFO_ENTRY_SYNTAX_VIOLATION_NAME_COUNT.get( e.getKey(), e.getValue().longValue())); } } } return Collections.unmodifiableList(messages); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy