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

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

Go to download

The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use Java API for communicating with LDAP directory servers and performing related tasks like reading and writing LDIF, encoding and decoding data using base64 and ASN.1 BER, and performing secure communication. This package contains the Standard Edition of the LDAP SDK, which is a complete, general-purpose library for communicating with LDAPv3 directory servers.

There is a newer version: 7.0.2
Show newest version
/*
 * Copyright 2008-2022 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2008-2022 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-2022 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 - 2025 Weber Informatics LLC | Privacy Policy