001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.schema; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.HashSet; 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033import java.util.Set; 034import java.util.TreeMap; 035import java.util.concurrent.ConcurrentHashMap; 036import java.util.concurrent.atomic.AtomicLong; 037import java.util.concurrent.atomic.AtomicReference; 038import java.util.regex.Pattern; 039 040import com.unboundid.asn1.ASN1OctetString; 041import com.unboundid.ldap.matchingrules.MatchingRule; 042import com.unboundid.ldap.sdk.Attribute; 043import com.unboundid.ldap.sdk.Entry; 044import com.unboundid.ldap.sdk.LDAPException; 045import com.unboundid.ldap.sdk.RDN; 046import com.unboundid.util.Debug; 047import com.unboundid.util.StaticUtils; 048import com.unboundid.util.ThreadSafety; 049import com.unboundid.util.ThreadSafetyLevel; 050import com.unboundid.util.Validator; 051 052import static com.unboundid.ldap.sdk.schema.SchemaMessages.*; 053 054 055 056/** 057 * This class provides a mechanism for validating entries against a schema. It 058 * provides the ability to customize the types of validation to perform, and can 059 * collect information about the entries that fail validation to provide a 060 * summary of the problems encountered. 061 * <BR><BR> 062 * The types of validation that may be performed for each entry include: 063 * <UL> 064 * <LI>Ensure that the entry has a valid DN.</LI> 065 * <LI>Ensure that all attribute values used in the entry's RDN are also 066 * present in the entry.</LI> 067 * <LI>Ensure that the entry has exactly one structural object class.</LI> 068 * <LI>Ensure that all of the object classes for the entry are defined in the 069 * schema.</LI> 070 * <LI>Ensure that all of the auxiliary classes for the entry are allowed by 071 * the DIT content rule for the entry's structural object class (if such a 072 * DIT content rule is defined).</LI> 073 * <LI>Ensure that all attributes contained in the entry are defined in the 074 * schema.</LI> 075 * <LI>Ensure that all attributes required by the entry's object classes or 076 * DIT content rule (if defined) are present in the entry.</LI> 077 * <LI>Ensure that all of the user attributes contained in the entry are 078 * allowed by the entry's object classes or DIT content rule (if 079 * defined).</LI> 080 * <LI>Ensure that all attribute values conform to the requirements of the 081 * associated attribute syntax.</LI> 082 * <LI>Ensure that all attributes with multiple values are defined as 083 * multi-valued in the associated schema.</LI> 084 * <LI>If there is a name form associated with the entry's structural object 085 * class, then ensure that the entry's RDN satisfies its constraints.</LI> 086 * </UL> 087 * All of these forms of validation will be performed by default, but individual 088 * types of validation may be enabled or disabled. 089 * <BR><BR> 090 * This class will not make any attempt to validate compliance with DIT 091 * structure rules, nor will it check the OBSOLETE field for any of the schema 092 * elements. In addition, attempts to validate whether attribute values 093 * conform to the syntax for the associated attribute type may only be 094 * completely accurate for syntaxes supported by the LDAP SDK. 095 * <BR><BR> 096 * This class is largely threadsafe, and the {@link EntryValidator#entryIsValid} 097 * is designed so that it can be invoked concurrently by multiple threads. 098 * Note, however, that it is not recommended that the any of the other methods 099 * in this class be used while any threads are running the {@code entryIsValid} 100 * method because changing the configuration or attempting to retrieve retrieve 101 * information may yield inaccurate or inconsistent results. 102 */ 103@ThreadSafety(level=ThreadSafetyLevel.MOSTLY_THREADSAFE) 104public final class EntryValidator 105 implements Serializable 106{ 107 /** 108 * The serial version UID for this serializable class. 109 */ 110 private static final long serialVersionUID = -8945609557086398241L; 111 112 113 114 // A count of the total number of entries examined. 115 private final AtomicLong entriesExamined; 116 117 // A count of the number of entries missing an attribute value contained in 118 // the RDN. 119 private final AtomicLong entriesMissingRDNValues; 120 121 // A count of the total number of invalid entries encountered. 122 private final AtomicLong invalidEntries; 123 124 // A count of the number of entries with DNs that could not be parsed. 125 private final AtomicLong malformedDNs; 126 127 // A count of the number of entries missing a superior object class. 128 private final AtomicLong missingSuperiorClasses; 129 130 // A count of the number of entries containing multiple structural object 131 // classes. 132 private final AtomicLong multipleStructuralClasses; 133 134 // A count of the number of entries with RDNs that violate the associated 135 // name form. 136 private final AtomicLong nameFormViolations; 137 138 // A count of the number of entries without any object class. 139 private final AtomicLong noObjectClasses; 140 141 // A count of the number of entries without a structural object class. 142 private final AtomicLong noStructuralClass; 143 144 // Indicates whether an entry should be considered invalid if it contains an 145 // attribute value which violates the associated attribute syntax. 146 private boolean checkAttributeSyntax; 147 148 // Indicates whether an entry should be considered invalid if it contains one 149 // or more attribute values in its RDN that are not present in the set of 150 // entry attributes. 151 private boolean checkEntryMissingRDNValues; 152 153 // Indicates whether an entry should be considered invalid if its DN cannot be 154 // parsed. 155 private boolean checkMalformedDNs; 156 157 // Indicates whether an entry should be considered invalid if it is missing 158 // attributes required by its object classes or DIT content rule. 159 private boolean checkMissingAttributes; 160 161 // Indicates whether an entry should be considered invalid if it is missing 162 // one or more superior object classes. 163 private boolean checkMissingSuperiorObjectClasses; 164 165 // Indicates whether an entry should be considered invalid if its RDN does not 166 // conform to name form requirements. 167 private boolean checkNameForms; 168 169 // Indicates whether an entry should be considered invalid if it contains any 170 // attributes which are not allowed by its object classes or DIT content rule. 171 private boolean checkProhibitedAttributes; 172 173 // Indicates whether an entry should be considered invalid if it contains an 174 // auxiliary class that is not allowed by its DIT content rule or an abstract 175 // class that is not associated with a non-abstract class. 176 private boolean checkProhibitedObjectClasses; 177 178 // Indicates whether an entry should be considered invalid if it contains any 179 // attribute defined as single-valued with more than one values. 180 private boolean checkSingleValuedAttributes; 181 182 // Indicates whether an entry should be considered invalid if it does not 183 // contain exactly one structural object class. 184 private boolean checkStructuralObjectClasses; 185 186 // Indicates whether an entry should be considered invalid if it contains an 187 // attribute which is not defined in the schema. 188 private boolean checkUndefinedAttributes; 189 190 // Indicates whether an entry should be considered invalid if it contains an 191 // object class which is not defined in the schema. 192 private boolean checkUndefinedObjectClasses; 193 194 // A map of the attributes with values violating the associated syntax to the 195 // number of values found violating the syntax. 196 private final ConcurrentHashMap<String,AtomicLong> attributesViolatingSyntax; 197 198 // A map of the required attribute types that were missing from entries to 199 // the number of entries missing them. 200 private final ConcurrentHashMap<String,AtomicLong> missingAttributes; 201 202 // A map of the prohibited attribute types that were included in entries to 203 // the number of entries referencing them. 204 private final ConcurrentHashMap<String,AtomicLong> prohibitedAttributes; 205 206 // A map of the prohibited auxiliary object classes that were included in 207 // entries to the number of entries referencing them. 208 private final ConcurrentHashMap<String,AtomicLong> prohibitedObjectClasses; 209 210 // A map of the single-valued attributes with multiple values to the number 211 // of entries with multiple values for those attributes. 212 private final ConcurrentHashMap<String,AtomicLong> singleValueViolations; 213 214 // A map of undefined attribute types to the number of entries referencing 215 // them. 216 private final ConcurrentHashMap<String,AtomicLong> undefinedAttributes; 217 218 // A map of undefined object classes to the number of entries referencing 219 // them. 220 private final ConcurrentHashMap<String,AtomicLong> undefinedObjectClasses; 221 222 // The schema against which entries will be validated. 223 private final Schema schema; 224 225 // The attribute types for which to ignore syntax violations. 226 private Set<AttributeTypeDefinition> ignoreSyntaxViolationTypes; 227 228 229 230 /** 231 * Creates a new entry validator that will validate entries according to the 232 * provided schema. 233 * 234 * @param schema The schema against which entries will be validated. 235 */ 236 public EntryValidator(final Schema schema) 237 { 238 this.schema = schema; 239 240 checkAttributeSyntax = true; 241 checkEntryMissingRDNValues = true; 242 checkMalformedDNs = true; 243 checkMissingAttributes = true; 244 checkMissingSuperiorObjectClasses = true; 245 checkNameForms = true; 246 checkProhibitedAttributes = true; 247 checkProhibitedObjectClasses = true; 248 checkSingleValuedAttributes = true; 249 checkStructuralObjectClasses = true; 250 checkUndefinedAttributes = true; 251 checkUndefinedObjectClasses = true; 252 253 ignoreSyntaxViolationTypes = Collections.emptySet(); 254 255 entriesExamined = new AtomicLong(0L); 256 entriesMissingRDNValues = new AtomicLong(0L); 257 invalidEntries = new AtomicLong(0L); 258 malformedDNs = new AtomicLong(0L); 259 missingSuperiorClasses = new AtomicLong(0L); 260 multipleStructuralClasses = new AtomicLong(0L); 261 nameFormViolations = new AtomicLong(0L); 262 noObjectClasses = new AtomicLong(0L); 263 noStructuralClass = new AtomicLong(0L); 264 265 attributesViolatingSyntax = 266 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); 267 missingAttributes = 268 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); 269 prohibitedAttributes = 270 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); 271 prohibitedObjectClasses = 272 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); 273 singleValueViolations = 274 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); 275 undefinedAttributes = 276 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); 277 undefinedObjectClasses = 278 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(20)); 279 } 280 281 282 283 /** 284 * Indicates whether the entry validator should consider entries invalid if 285 * they are missing attributes which are required by the object classes or 286 * DIT content rule (if applicable) for the entry. 287 * 288 * @return {@code true} if entries that are missing attributes required by 289 * its object classes or DIT content rule should be considered 290 * invalid, or {@code false} if not. 291 */ 292 public boolean checkMissingAttributes() 293 { 294 return checkMissingAttributes; 295 } 296 297 298 299 /** 300 * Specifies whether the entry validator should consider entries invalid if 301 * they are missing attributes which are required by the object classes or DIT 302 * content rule (if applicable) for the entry. 303 * 304 * @param checkMissingAttributes Indicates whether the entry validator 305 * should consider entries invalid if they are 306 * missing required attributes. 307 */ 308 public void setCheckMissingAttributes(final boolean checkMissingAttributes) 309 { 310 this.checkMissingAttributes = checkMissingAttributes; 311 } 312 313 314 315 /** 316 * Indicates whether the entry validator should consider entries invalid if 317 * they are missing any superior classes for the included set of object 318 * classes. 319 * 320 * @return {@code true} if entries that are missing superior classes should 321 * be considered invalid, or {@code false} if not. 322 */ 323 public boolean checkMissingSuperiorObjectClasses() 324 { 325 return checkMissingSuperiorObjectClasses; 326 } 327 328 329 330 /** 331 * Specifies whether the entry validator should consider entries invalid if 332 * they are missing any superior classes for the included set of object 333 * classes. 334 * 335 * @param checkMissingSuperiorObjectClasses Indicates whether the entry 336 * validator should consider 337 * entries invalid if they are 338 * missing any superior classes for 339 * the included set of object 340 * classes. 341 */ 342 public void setCheckMissingSuperiorObjectClasses( 343 final boolean checkMissingSuperiorObjectClasses) 344 { 345 this.checkMissingSuperiorObjectClasses = checkMissingSuperiorObjectClasses; 346 } 347 348 349 350 /** 351 * Indicates whether the entry validator should consider entries invalid if 352 * their DNs cannot be parsed. 353 * 354 * @return {@code true} if entries with malformed DNs should be considered 355 * invalid, or {@code false} if not. 356 */ 357 public boolean checkMalformedDNs() 358 { 359 return checkMalformedDNs; 360 } 361 362 363 364 /** 365 * Specifies whether the entry validator should consider entries invalid if 366 * their DNs cannot be parsed. 367 * 368 * @param checkMalformedDNs Specifies whether entries with malformed DNs 369 * should be considered invalid. 370 */ 371 public void setCheckMalformedDNs(final boolean checkMalformedDNs) 372 { 373 this.checkMalformedDNs = checkMalformedDNs; 374 } 375 376 377 378 /** 379 * Indicates whether the entry validator should consider entries invalid if 380 * they contain one or more attribute values in their RDN that are not present 381 * in the set of entry attributes. 382 * 383 * @return {@code true} if entries missing one or more attribute values 384 * included in their RDNs should be considered invalid, or 385 * {@code false} if not. 386 */ 387 public boolean checkEntryMissingRDNValues() 388 { 389 return checkEntryMissingRDNValues; 390 } 391 392 393 394 /** 395 * Specifies whether the entry validator should consider entries invalid if 396 * they contain one or more attribute values in their RDN that are not present 397 * in the set of entry attributes. 398 * 399 * @param checkEntryMissingRDNValues Indicates whether the entry validator 400 * should consider entries invalid if they 401 * contain one or more attribute values in 402 * their RDN that are not present in the 403 * set of entry attributes. 404 */ 405 public void setCheckEntryMissingRDNValues( 406 final boolean checkEntryMissingRDNValues) 407 { 408 this.checkEntryMissingRDNValues = checkEntryMissingRDNValues; 409 } 410 411 412 413 /** 414 * Indicates whether the entry validator should consider entries invalid if 415 * the attributes contained in the RDN violate the constraints of the 416 * associated name form. 417 * 418 * @return {@code true} if entries with RDNs that do not conform to the 419 * associated name form should be considered invalid, or 420 * {@code false} if not. 421 */ 422 public boolean checkNameForms() 423 { 424 return checkNameForms; 425 } 426 427 428 429 /** 430 * Specifies whether the entry validator should consider entries invalid if 431 * the attributes contained in the RDN violate the constraints of the 432 * associated name form. 433 * 434 * @param checkNameForms Indicates whether the entry validator should 435 * consider entries invalid if their RDNs violate name 436 * form constraints. 437 */ 438 public void setCheckNameForms(final boolean checkNameForms) 439 { 440 this.checkNameForms = checkNameForms; 441 } 442 443 444 445 /** 446 * Indicates whether the entry validator should consider entries invalid if 447 * they contain attributes which are not allowed by (or are prohibited by) the 448 * object classes and DIT content rule (if applicable) for the entry. 449 * 450 * @return {@code true} if entries should be considered invalid if they 451 * contain attributes which are not allowed, or {@code false} if not. 452 */ 453 public boolean checkProhibitedAttributes() 454 { 455 return checkProhibitedAttributes; 456 } 457 458 459 460 /** 461 * Specifies whether the entry validator should consider entries invalid if 462 * they contain attributes which are not allowed by (or are prohibited by) the 463 * object classes and DIT content rule (if applicable) for the entry. 464 * 465 * @param checkProhibitedAttributes Indicates whether entries should be 466 * considered invalid if they contain 467 * attributes which are not allowed. 468 */ 469 public void setCheckProhibitedAttributes( 470 final boolean checkProhibitedAttributes) 471 { 472 this.checkProhibitedAttributes = checkProhibitedAttributes; 473 } 474 475 476 477 /** 478 * Indicates whether the entry validator should consider entries invalid if 479 * they contain auxiliary object classes which are not allowed by the DIT 480 * content rule (if applicable) for the entry, or if they contain any abstract 481 * object classes which are not subclassed by any non-abstract classes 482 * included in the entry. 483 * 484 * @return {@code true} if entries should be considered invalid if they 485 * contain prohibited object classes, or {@code false} if not. 486 */ 487 public boolean checkProhibitedObjectClasses() 488 { 489 return checkProhibitedObjectClasses; 490 } 491 492 493 494 /** 495 * Specifies whether the entry validator should consider entries invalid if 496 * they contain auxiliary object classes which are not allowed by the DIT 497 * content rule (if applicable) for the entry, or if they contain any abstract 498 * object classes which are not subclassed by any non-abstract classes 499 * included in the entry. 500 * 501 * @param checkProhibitedObjectClasses Indicates whether entries should be 502 * considered invalid if they contain 503 * prohibited object classes. 504 */ 505 public void setCheckProhibitedObjectClasses( 506 final boolean checkProhibitedObjectClasses) 507 { 508 this.checkProhibitedObjectClasses = checkProhibitedObjectClasses; 509 } 510 511 512 513 /** 514 * Indicates whether the entry validator should consider entries invalid if 515 * they they contain attributes with more than one value which are declared as 516 * single-valued in the schema. 517 * 518 * @return {@code true} if entries should be considered invalid if they 519 * contain single-valued attributes with more than one value, or 520 * {@code false} if not. 521 */ 522 public boolean checkSingleValuedAttributes() 523 { 524 return checkSingleValuedAttributes; 525 } 526 527 528 529 /** 530 * Specifies whether the entry validator should consider entries invalid if 531 * they contain attributes with more than one value which are declared as 532 * single-valued in the schema. 533 * 534 * @param checkSingleValuedAttributes Indicates whether entries should be 535 * considered invalid if they contain 536 * single-valued attributes with more 537 * than one value. 538 */ 539 public void setCheckSingleValuedAttributes( 540 final boolean checkSingleValuedAttributes) 541 { 542 this.checkSingleValuedAttributes = checkSingleValuedAttributes; 543 } 544 545 546 547 /** 548 * Indicates whether the entry validator should consider entries invalid if 549 * they do not contain exactly one structural object class (i.e., either do 550 * not have any structural object class, or have more than one). 551 * 552 * @return {@code true} if entries should be considered invalid if they do 553 * not have exactly one structural object class, or {@code false} if 554 * not. 555 */ 556 public boolean checkStructuralObjectClasses() 557 { 558 return checkStructuralObjectClasses; 559 } 560 561 562 563 /** 564 * Specifies whether the entry validator should consider entries invalid if 565 * they do not contain exactly one structural object class (i.e., either do 566 * not have any structural object class, or have more than one). 567 * 568 * @param checkStructuralObjectClasses Indicates whether entries should be 569 * considered invalid if they do not 570 * have exactly one structural object 571 * class. 572 */ 573 public void setCheckStructuralObjectClasses( 574 final boolean checkStructuralObjectClasses) 575 { 576 this.checkStructuralObjectClasses = checkStructuralObjectClasses; 577 } 578 579 580 581 /** 582 * Indicates whether the entry validator should consider entries invalid if 583 * they contain attributes which violate the associated attribute syntax. 584 * 585 * @return {@code true} if entries should be considered invalid if they 586 * contain attribute values which violate the associated attribute 587 * syntax, or {@code false} if not. 588 */ 589 public boolean checkAttributeSyntax() 590 { 591 return checkAttributeSyntax; 592 } 593 594 595 596 /** 597 * Specifies whether the entry validator should consider entries invalid if 598 * they contain attributes which violate the associated attribute syntax. 599 * 600 * @param checkAttributeSyntax Indicates whether entries should be 601 * considered invalid if they violate the 602 * associated attribute syntax. 603 */ 604 public void setCheckAttributeSyntax(final boolean checkAttributeSyntax) 605 { 606 this.checkAttributeSyntax = checkAttributeSyntax; 607 } 608 609 610 611 /** 612 * Retrieves the set of attribute types for which syntax violations should be 613 * ignored. If {@link #checkAttributeSyntax()} returns {@code true}, then 614 * any attribute syntax violations will be flagged for all attributes except 615 * those attributes in this set. If {@code checkAttributeSyntax()} returns 616 * {@code false}, then all syntax violations will be ignored. 617 * 618 * @return The set of attribute types for which syntax violations should be 619 * ignored. 620 */ 621 public Set<AttributeTypeDefinition> getIgnoreSyntaxViolationsAttributeTypes() 622 { 623 return ignoreSyntaxViolationTypes; 624 } 625 626 627 628 /** 629 * Specifies the set of attribute types for which syntax violations should be 630 * ignored. This method will only have any effect if 631 * {@link #checkAttributeSyntax()} returns {@code true}. 632 * 633 * @param attributeTypes The definitions for the attribute types for which 634 * to ignore syntax violations. It may be 635 * {@code null} or empty if no violations should be 636 * ignored. 637 */ 638 public void setIgnoreSyntaxViolationAttributeTypes( 639 final AttributeTypeDefinition... attributeTypes) 640 { 641 if (attributeTypes == null) 642 { 643 ignoreSyntaxViolationTypes = Collections.emptySet(); 644 } 645 else 646 { 647 ignoreSyntaxViolationTypes = Collections.unmodifiableSet( 648 new HashSet<>(StaticUtils.toList(attributeTypes))); 649 } 650 } 651 652 653 654 /** 655 * Specifies the names or OIDs of the attribute types for which syntax 656 * violations should be ignored. This method will only have any effect if 657 * {@link #checkAttributeSyntax()} returns {@code true}. 658 * 659 * @param attributeTypes The names or OIDs of the attribute types for which 660 * to ignore syntax violations. It may be 661 * {@code null} or empty if no violations should be 662 * ignored. 663 */ 664 public void setIgnoreSyntaxViolationAttributeTypes( 665 final String... attributeTypes) 666 { 667 setIgnoreSyntaxViolationAttributeTypes(StaticUtils.toList(attributeTypes)); 668 } 669 670 671 672 /** 673 * Specifies the names or OIDs of the attribute types for which syntax 674 * violations should be ignored. This method will only have any effect if 675 * {@link #checkAttributeSyntax()} returns {@code true}. 676 * 677 * @param attributeTypes The names or OIDs of the attribute types for which 678 * to ignore syntax violations. It may be 679 * {@code null} or empty if no violations should be 680 * ignored. Any attribute types not defined in the 681 * schema will be ignored. 682 */ 683 public void setIgnoreSyntaxViolationAttributeTypes( 684 final Collection<String> attributeTypes) 685 { 686 if (attributeTypes == null) 687 { 688 ignoreSyntaxViolationTypes = Collections.emptySet(); 689 return; 690 } 691 692 final HashSet<AttributeTypeDefinition> atSet = 693 new HashSet<>(StaticUtils.computeMapCapacity(attributeTypes.size())); 694 for (final String s : attributeTypes) 695 { 696 final AttributeTypeDefinition d = schema.getAttributeType(s); 697 if (d != null) 698 { 699 atSet.add(d); 700 } 701 } 702 703 ignoreSyntaxViolationTypes = Collections.unmodifiableSet(atSet); 704 } 705 706 707 708 /** 709 * Indicates whether the entry validator should consider entries invalid if 710 * they contain attributes which are not defined in the schema. 711 * 712 * @return {@code true} if entries should be considered invalid if they 713 * contain attributes which are not defined in the schema, or 714 * {@code false} if not. 715 */ 716 public boolean checkUndefinedAttributes() 717 { 718 return checkUndefinedAttributes; 719 } 720 721 722 723 /** 724 * Specifies whether the entry validator should consider entries invalid if 725 * they contain attributes which are not defined in the schema. 726 * 727 * @param checkUndefinedAttributes Indicates whether entries should be 728 * considered invalid if they contain 729 * attributes which are not defined in the 730 * schema, or {@code false} if not. 731 */ 732 public void setCheckUndefinedAttributes( 733 final boolean checkUndefinedAttributes) 734 { 735 this.checkUndefinedAttributes = checkUndefinedAttributes; 736 } 737 738 739 740 /** 741 * Indicates whether the entry validator should consider entries invalid if 742 * they contain object classes which are not defined in the schema. 743 * 744 * @return {@code true} if entries should be considered invalid if they 745 * contain object classes which are not defined in the schema, or 746 * {@code false} if not. 747 */ 748 public boolean checkUndefinedObjectClasses() 749 { 750 return checkUndefinedObjectClasses; 751 } 752 753 754 755 /** 756 * Specifies whether the entry validator should consider entries invalid if 757 * they contain object classes which are not defined in the schema. 758 * 759 * @param checkUndefinedObjectClasses Indicates whether entries should be 760 * considered invalid if they contain 761 * object classes which are not defined 762 * in the schema. 763 */ 764 public void setCheckUndefinedObjectClasses( 765 final boolean checkUndefinedObjectClasses) 766 { 767 this.checkUndefinedObjectClasses = checkUndefinedObjectClasses; 768 } 769 770 771 772 /** 773 * Indicates whether the provided entry passes all of the enabled types of 774 * validation. 775 * 776 * @param entry The entry to be examined. It must not be 777 * {@code null}. 778 * @param invalidReasons A list to which messages may be added which provide 779 * information about why the entry is invalid. It may 780 * be {@code null} if this information is not needed. 781 * 782 * @return {@code true} if the entry conforms to all of the enabled forms of 783 * validation, or {@code false} if the entry fails at least one of 784 * the tests. 785 */ 786 public boolean entryIsValid(final Entry entry, 787 final List<String> invalidReasons) 788 { 789 Validator.ensureNotNull(entry); 790 791 boolean entryValid = true; 792 entriesExamined.incrementAndGet(); 793 794 // Get the parsed DN for the entry. 795 RDN rdn = null; 796 try 797 { 798 rdn = entry.getParsedDN().getRDN(); 799 } 800 catch (final LDAPException le) 801 { 802 Debug.debugException(le); 803 if (checkMalformedDNs) 804 { 805 entryValid = false; 806 malformedDNs.incrementAndGet(); 807 if (invalidReasons != null) 808 { 809 invalidReasons.add(ERR_ENTRY_MALFORMED_DN.get( 810 StaticUtils.getExceptionMessage(le))); 811 } 812 } 813 } 814 815 // Get the object class descriptions for the object classes in the entry. 816 final HashSet<ObjectClassDefinition> ocSet = 817 new HashSet<>(StaticUtils.computeMapCapacity(10)); 818 final boolean missingOC = 819 (! getObjectClasses(entry, ocSet, invalidReasons)); 820 if (missingOC) 821 { 822 entryValid = false; 823 } 824 825 // If the entry was not missing any object classes, then get the structural 826 // class for the entry and use it to get the associated DIT content rule and 827 // name form. 828 DITContentRuleDefinition ditContentRule = null; 829 NameFormDefinition nameForm = null; 830 if (! missingOC) 831 { 832 final AtomicReference<ObjectClassDefinition> ref = 833 new AtomicReference<>(null); 834 entryValid &= getStructuralClass(ocSet, ref, invalidReasons); 835 final ObjectClassDefinition structuralClass = ref.get(); 836 if (structuralClass != null) 837 { 838 ditContentRule = schema.getDITContentRule(structuralClass.getOID()); 839 nameForm = 840 schema.getNameFormByObjectClass(structuralClass.getNameOrOID()); 841 } 842 } 843 844 // If we should check for missing required attributes, then do so. 845 HashSet<AttributeTypeDefinition> requiredAttrs = null; 846 if (checkMissingAttributes || checkProhibitedAttributes) 847 { 848 requiredAttrs = getRequiredAttributes(ocSet, ditContentRule); 849 if (checkMissingAttributes) 850 { 851 entryValid &= checkForMissingAttributes(entry, rdn, requiredAttrs, 852 invalidReasons); 853 } 854 } 855 856 // Iterate through all of the attributes in the entry. Make sure that they 857 // are all defined in the schema, that they are allowed to be present in the 858 // entry, that their values conform to the associated syntax, and that any 859 // single-valued attributes have only one value. 860 HashSet<AttributeTypeDefinition> optionalAttrs = null; 861 if (checkProhibitedAttributes) 862 { 863 optionalAttrs = 864 getOptionalAttributes(ocSet, ditContentRule, requiredAttrs); 865 } 866 for (final Attribute a : entry.getAttributes()) 867 { 868 entryValid &= 869 checkAttribute(a, requiredAttrs, optionalAttrs, invalidReasons); 870 } 871 872 // If there is a DIT content rule, then check to ensure that all of the 873 // auxiliary object classes are allowed. 874 if (checkProhibitedObjectClasses && (ditContentRule != null)) 875 { 876 entryValid &= 877 checkAuxiliaryClasses(ocSet, ditContentRule, invalidReasons); 878 } 879 880 // Check the entry's RDN to ensure that all attributes are defined in the 881 // schema, allowed to be present, and comply with the name form. 882 if (rdn != null) 883 { 884 entryValid &= checkRDN(rdn, entry, requiredAttrs, optionalAttrs, nameForm, 885 invalidReasons); 886 } 887 888 if (! entryValid) 889 { 890 invalidEntries.incrementAndGet(); 891 } 892 893 return entryValid; 894 } 895 896 897 898 /** 899 * Gets the object classes for the entry, including any that weren't 900 * explicitly included but should be because they were superior to classes 901 * that were included. 902 * 903 * @param entry The entry to examine. 904 * @param ocSet The set into which the object class definitions 905 * should be placed. 906 * @param invalidReasons A list to which messages may be added which provide 907 * information about why the entry is invalid. It may 908 * be {@code null} if this information is not needed. 909 * 910 * @return {@code true} if the entry passed all validation processing 911 * performed by this method, or {@code false} if there were any 912 * failures. 913 */ 914 private boolean getObjectClasses(final Entry entry, 915 final HashSet<ObjectClassDefinition> ocSet, 916 final List<String> invalidReasons) 917 { 918 final String[] ocValues = entry.getObjectClassValues(); 919 if ((ocValues == null) || (ocValues.length == 0)) 920 { 921 noObjectClasses.incrementAndGet(); 922 if (invalidReasons != null) 923 { 924 invalidReasons.add(ERR_ENTRY_NO_OCS.get()); 925 } 926 return false; 927 } 928 929 boolean entryValid = true; 930 final HashSet<String> missingOCs = 931 new HashSet<>(StaticUtils.computeMapCapacity(ocValues.length)); 932 for (final String ocName : entry.getObjectClassValues()) 933 { 934 final ObjectClassDefinition d = schema.getObjectClass(ocName); 935 if (d == null) 936 { 937 if (checkUndefinedObjectClasses) 938 { 939 entryValid = false; 940 missingOCs.add(StaticUtils.toLowerCase(ocName)); 941 updateCount(ocName, undefinedObjectClasses); 942 if (invalidReasons != null) 943 { 944 invalidReasons.add(ERR_ENTRY_UNDEFINED_OC.get(ocName)); 945 } 946 } 947 } 948 else 949 { 950 ocSet.add(d); 951 } 952 } 953 954 for (final ObjectClassDefinition d : new HashSet<>(ocSet)) 955 { 956 entryValid &= addSuperiorClasses(d, ocSet, missingOCs, invalidReasons); 957 } 958 959 return entryValid; 960 } 961 962 963 964 /** 965 * Recursively adds the definition superior class for the provided object 966 * class definition to the provided set, if it is not already present. 967 * 968 * @param d The object class definition to process. 969 * @param ocSet The set into which the object class definitions 970 * should be placed. 971 * @param missingOCNames The names of the object classes we already know are 972 * missing and therefore shouldn't be flagged again. 973 * @param invalidReasons A list to which messages may be added which provide 974 * information about why the entry is invalid. It may 975 * be {@code null} if this information is not needed. 976 * 977 * @return {@code true} if the entry passed all validation processing 978 * performed by this method, or {@code false} if there were any 979 * failures. 980 */ 981 private boolean addSuperiorClasses(final ObjectClassDefinition d, 982 final HashSet<ObjectClassDefinition> ocSet, 983 final HashSet<String> missingOCNames, 984 final List<String> invalidReasons) 985 { 986 boolean entryValid = true; 987 988 for (final String ocName : d.getSuperiorClasses()) 989 { 990 final ObjectClassDefinition supOC = schema.getObjectClass(ocName); 991 if (supOC == null) 992 { 993 if (checkUndefinedObjectClasses) 994 { 995 entryValid = false; 996 final String lowerName = StaticUtils.toLowerCase(ocName); 997 if (! missingOCNames.contains(lowerName)) 998 { 999 missingOCNames.add(lowerName); 1000 updateCount(ocName, undefinedObjectClasses); 1001 if (invalidReasons != null) 1002 { 1003 invalidReasons.add(ERR_ENTRY_UNDEFINED_SUP_OC.get( 1004 d.getNameOrOID(), ocName)); 1005 } 1006 } 1007 } 1008 } 1009 else 1010 { 1011 if (! ocSet.contains(supOC)) 1012 { 1013 ocSet.add(supOC); 1014 if (checkMissingSuperiorObjectClasses) 1015 { 1016 entryValid = false; 1017 missingSuperiorClasses.incrementAndGet(); 1018 if (invalidReasons != null) 1019 { 1020 invalidReasons.add(ERR_ENTRY_MISSING_SUP_OC.get( 1021 supOC.getNameOrOID(), d.getNameOrOID())); 1022 } 1023 } 1024 } 1025 1026 entryValid &= 1027 addSuperiorClasses(supOC, ocSet, missingOCNames, invalidReasons); 1028 } 1029 } 1030 1031 return entryValid; 1032 } 1033 1034 1035 1036 /** 1037 * Retrieves the structural object class from the set of provided object 1038 * classes. 1039 * 1040 * @param ocSet The set of object class definitions for the entry. 1041 * @param structuralClass The reference that will be updated with the 1042 * entry's structural object class. 1043 * @param invalidReasons A list to which messages may be added which 1044 * provide provide information about why the entry is 1045 * invalid. It may be {@code null} if this 1046 * information is not needed. 1047 * 1048 * @return {@code true} if the entry passes all validation checks performed 1049 * by this method, or {@code false} if not. 1050 */ 1051 private boolean getStructuralClass(final HashSet<ObjectClassDefinition> ocSet, 1052 final AtomicReference<ObjectClassDefinition> structuralClass, 1053 final List<String> invalidReasons) 1054 { 1055 final HashSet<ObjectClassDefinition> ocCopy = new HashSet<>(ocSet); 1056 for (final ObjectClassDefinition d : ocSet) 1057 { 1058 final ObjectClassType t = d.getObjectClassType(schema); 1059 if (t == ObjectClassType.STRUCTURAL) 1060 { 1061 ocCopy.removeAll(d.getSuperiorClasses(schema, true)); 1062 } 1063 else if (t == ObjectClassType.AUXILIARY) 1064 { 1065 ocCopy.remove(d); 1066 ocCopy.removeAll(d.getSuperiorClasses(schema, true)); 1067 } 1068 } 1069 1070 // Iterate through the set of remaining classes and strip out any 1071 // abstract classes. 1072 boolean entryValid = true; 1073 Iterator<ObjectClassDefinition> iterator = ocCopy.iterator(); 1074 while (iterator.hasNext()) 1075 { 1076 final ObjectClassDefinition d = iterator.next(); 1077 if (d.getObjectClassType(schema) == ObjectClassType.ABSTRACT) 1078 { 1079 if (checkProhibitedObjectClasses) 1080 { 1081 entryValid = false; 1082 updateCount(d.getNameOrOID(), prohibitedObjectClasses); 1083 if (invalidReasons != null) 1084 { 1085 invalidReasons.add(ERR_ENTRY_INVALID_ABSTRACT_CLASS.get( 1086 d.getNameOrOID())); 1087 } 1088 } 1089 iterator.remove(); 1090 } 1091 } 1092 1093 switch (ocCopy.size()) 1094 { 1095 case 0: 1096 if (checkStructuralObjectClasses) 1097 { 1098 entryValid = false; 1099 noStructuralClass.incrementAndGet(); 1100 if (invalidReasons != null) 1101 { 1102 invalidReasons.add(ERR_ENTRY_NO_STRUCTURAL_CLASS.get()); 1103 } 1104 } 1105 break; 1106 1107 case 1: 1108 structuralClass.set(ocCopy.iterator().next()); 1109 break; 1110 1111 default: 1112 if (checkStructuralObjectClasses) 1113 { 1114 entryValid = false; 1115 multipleStructuralClasses.incrementAndGet(); 1116 if (invalidReasons != null) 1117 { 1118 final StringBuilder ocList = new StringBuilder(); 1119 iterator = ocCopy.iterator(); 1120 while (iterator.hasNext()) 1121 { 1122 ocList.append(iterator.next().getNameOrOID()); 1123 if (iterator.hasNext()) 1124 { 1125 ocList.append(", "); 1126 } 1127 } 1128 invalidReasons.add( 1129 ERR_ENTRY_MULTIPLE_STRUCTURAL_CLASSES.get(ocList)); 1130 } 1131 } 1132 break; 1133 } 1134 1135 return entryValid; 1136 } 1137 1138 1139 1140 /** 1141 * Retrieves the set of attributes which must be present in entries with the 1142 * provided set of object classes and DIT content rule. 1143 * 1144 * @param ocSet The set of object classes for the entry. 1145 * @param ditContentRule The DIT content rule for the entry, if defined. 1146 * 1147 * @return The set of attributes which must be present in entries with the 1148 * provided set of object classes and DIT content rule. 1149 */ 1150 private HashSet<AttributeTypeDefinition> getRequiredAttributes( 1151 final HashSet<ObjectClassDefinition> ocSet, 1152 final DITContentRuleDefinition ditContentRule) 1153 { 1154 final HashSet<AttributeTypeDefinition> attrSet = 1155 new HashSet<>(StaticUtils.computeMapCapacity(20)); 1156 for (final ObjectClassDefinition oc : ocSet) 1157 { 1158 attrSet.addAll(oc.getRequiredAttributes(schema, false)); 1159 } 1160 1161 if (ditContentRule != null) 1162 { 1163 for (final String s : ditContentRule.getRequiredAttributes()) 1164 { 1165 final AttributeTypeDefinition d = schema.getAttributeType(s); 1166 if (d != null) 1167 { 1168 attrSet.add(d); 1169 } 1170 } 1171 } 1172 1173 return attrSet; 1174 } 1175 1176 1177 1178 /** 1179 * Retrieves the set of attributes which may optionally be present in entries 1180 * with the provided set of object classes and DIT content rule. 1181 * 1182 * @param ocSet The set of object classes for the entry. 1183 * @param ditContentRule The DIT content rule for the entry, if defined. 1184 * @param requiredAttrSet The set of required attributes for the entry. 1185 * 1186 * @return The set of attributes which may optionally be present in entries 1187 * with the provided set of object classes and DIT content rule. 1188 */ 1189 private HashSet<AttributeTypeDefinition> getOptionalAttributes( 1190 final HashSet<ObjectClassDefinition> ocSet, 1191 final DITContentRuleDefinition ditContentRule, 1192 final HashSet<AttributeTypeDefinition> requiredAttrSet) 1193 { 1194 final HashSet<AttributeTypeDefinition> attrSet = 1195 new HashSet<>(StaticUtils.computeMapCapacity(20)); 1196 for (final ObjectClassDefinition oc : ocSet) 1197 { 1198 if (oc.hasNameOrOID("extensibleObject") || 1199 oc.hasNameOrOID("1.3.6.1.4.1.1466.101.120.111")) 1200 { 1201 attrSet.addAll(schema.getUserAttributeTypes()); 1202 break; 1203 } 1204 1205 for (final AttributeTypeDefinition d : 1206 oc.getOptionalAttributes(schema, false)) 1207 { 1208 if (! requiredAttrSet.contains(d)) 1209 { 1210 attrSet.add(d); 1211 } 1212 } 1213 } 1214 1215 if (ditContentRule != null) 1216 { 1217 for (final String s : ditContentRule.getOptionalAttributes()) 1218 { 1219 final AttributeTypeDefinition d = schema.getAttributeType(s); 1220 if ((d != null) && (! requiredAttrSet.contains(d))) 1221 { 1222 attrSet.add(d); 1223 } 1224 } 1225 1226 for (final String s : ditContentRule.getProhibitedAttributes()) 1227 { 1228 final AttributeTypeDefinition d = schema.getAttributeType(s); 1229 if (d != null) 1230 { 1231 attrSet.remove(d); 1232 } 1233 } 1234 } 1235 1236 return attrSet; 1237 } 1238 1239 1240 1241 /** 1242 * Checks the provided entry to determine whether it is missing any required 1243 * attributes. 1244 * 1245 * @param entry The entry to examine. 1246 * @param rdn The RDN for the entry, if available. 1247 * @param requiredAttrs The set of attribute types which are required to be 1248 * included in the entry. 1249 * @param invalidReasons A list to which messages may be added which provide 1250 * information about why the entry is invalid. It may 1251 * be {@code null} if this information is not needed. 1252 * 1253 * @return {@code true} if the entry has all required attributes, or 1254 * {@code false} if not. 1255 */ 1256 private boolean checkForMissingAttributes(final Entry entry, final RDN rdn, 1257 final HashSet<AttributeTypeDefinition> requiredAttrs, 1258 final List<String> invalidReasons) 1259 { 1260 boolean entryValid = true; 1261 1262 for (final AttributeTypeDefinition d : requiredAttrs) 1263 { 1264 boolean found = false; 1265 for (final String s : d.getNames()) 1266 { 1267 if (entry.hasAttribute(s) || ((rdn != null) && rdn.hasAttribute(s))) 1268 { 1269 found = true; 1270 break; 1271 } 1272 } 1273 1274 if (! found) 1275 { 1276 if (! (entry.hasAttribute(d.getOID()) || 1277 ((rdn != null) && (rdn.hasAttribute(d.getOID()))))) 1278 { 1279 entryValid = false; 1280 updateCount(d.getNameOrOID(), missingAttributes); 1281 if (invalidReasons != null) 1282 { 1283 invalidReasons.add(ERR_ENTRY_MISSING_REQUIRED_ATTR.get( 1284 d.getNameOrOID())); 1285 } 1286 } 1287 } 1288 } 1289 1290 return entryValid; 1291 } 1292 1293 1294 1295 /** 1296 * Checks the provided attribute to determine whether it appears to be valid. 1297 * 1298 * @param attr The attribute to examine. 1299 * @param requiredAttrs The set of attribute types which are required to be 1300 * included in the entry. 1301 * @param optionalAttrs The set of attribute types which may optionally be 1302 * included in the entry. 1303 * @param invalidReasons A list to which messages may be added which provide 1304 * information about why the entry is invalid. It may 1305 * be {@code null} if this information is not needed. 1306 * 1307 * @return {@code true} if the attribute passed all of the checks and appears 1308 * to be valid, or {@code false} if it failed any of the checks. 1309 */ 1310 private boolean checkAttribute(final Attribute attr, 1311 final HashSet<AttributeTypeDefinition> requiredAttrs, 1312 final HashSet<AttributeTypeDefinition> optionalAttrs, 1313 final List<String> invalidReasons) 1314 { 1315 boolean entryValid = true; 1316 1317 final AttributeTypeDefinition d = 1318 schema.getAttributeType(attr.getBaseName()); 1319 if (d == null) 1320 { 1321 if (checkUndefinedAttributes) 1322 { 1323 entryValid = false; 1324 updateCount(attr.getBaseName(), undefinedAttributes); 1325 if (invalidReasons != null) 1326 { 1327 invalidReasons.add(ERR_ENTRY_UNDEFINED_ATTR.get(attr.getBaseName())); 1328 } 1329 } 1330 1331 return entryValid; 1332 } 1333 1334 if (checkProhibitedAttributes && (! d.isOperational())) 1335 { 1336 if (! (requiredAttrs.contains(d) || optionalAttrs.contains(d))) 1337 { 1338 entryValid = false; 1339 updateCount(d.getNameOrOID(), prohibitedAttributes); 1340 if (invalidReasons != null) 1341 { 1342 invalidReasons.add(ERR_ENTRY_ATTR_NOT_ALLOWED.get(d.getNameOrOID())); 1343 } 1344 } 1345 } 1346 1347 final ASN1OctetString[] rawValues = attr.getRawValues(); 1348 if (checkSingleValuedAttributes && d.isSingleValued() && 1349 (rawValues.length > 1)) 1350 { 1351 entryValid = false; 1352 updateCount(d.getNameOrOID(), singleValueViolations); 1353 if (invalidReasons != null) 1354 { 1355 invalidReasons.add( 1356 ERR_ENTRY_ATTR_HAS_MULTIPLE_VALUES.get(d.getNameOrOID())); 1357 } 1358 } 1359 1360 if (checkAttributeSyntax) 1361 { 1362 if (! ignoreSyntaxViolationTypes.contains(d)) 1363 { 1364 final MatchingRule r = 1365 MatchingRule.selectEqualityMatchingRule(d.getNameOrOID(), schema); 1366 final Map<String, String[]> extensions = d.getExtensions(); 1367 for (final ASN1OctetString v : rawValues) 1368 { 1369 try 1370 { 1371 r.normalize(v); 1372 } 1373 catch (final LDAPException le) 1374 { 1375 Debug.debugException(le); 1376 entryValid = false; 1377 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1378 if (invalidReasons != null) 1379 { 1380 invalidReasons.add(ERR_ENTRY_ATTR_INVALID_SYNTAX.get( 1381 v.stringValue(), d.getNameOrOID(), 1382 StaticUtils.getExceptionMessage(le))); 1383 } 1384 } 1385 1386 1387 // If the attribute type definition includes an X-ALLOWED-VALUE 1388 // extension, then make sure the value is in that set. 1389 final String[] allowedValues = extensions.get("X-ALLOWED-VALUE"); 1390 if (allowedValues != null) 1391 { 1392 boolean isAllowed = false; 1393 for (final String allowedValue : allowedValues) 1394 { 1395 try 1396 { 1397 if (r.valuesMatch(v, new ASN1OctetString(allowedValue))) 1398 { 1399 isAllowed = true; 1400 break; 1401 } 1402 } 1403 catch (final Exception e) 1404 { 1405 Debug.debugException(e); 1406 } 1407 } 1408 1409 if (! isAllowed) 1410 { 1411 entryValid = false; 1412 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1413 if (invalidReasons != null) 1414 { 1415 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_ALLOWED.get( 1416 v.stringValue(), d.getNameOrOID())); 1417 } 1418 } 1419 } 1420 1421 1422 // If the attribute type definition includes an X-VALUE-REGEX 1423 // extension, then make sure the value matches one of those regexes. 1424 final String[] valueRegexes = extensions.get("X-VALUE-REGEX"); 1425 if (valueRegexes != null) 1426 { 1427 boolean matchesRegex = false; 1428 for (final String regex : valueRegexes) 1429 { 1430 try 1431 { 1432 final Pattern pattern = Pattern.compile(regex); 1433 if (pattern.matcher(v.stringValue()).matches()) 1434 { 1435 matchesRegex = true; 1436 break; 1437 } 1438 } 1439 catch (final Exception e) 1440 { 1441 Debug.debugException(e); 1442 } 1443 } 1444 1445 if (! matchesRegex) 1446 { 1447 entryValid = false; 1448 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1449 if (invalidReasons != null) 1450 { 1451 invalidReasons.add( 1452 ERR_ENTRY_ATTR_VALUE_NOT_ALLOWED_BY_REGEX.get( 1453 v.stringValue(), d.getNameOrOID())); 1454 } 1455 } 1456 } 1457 1458 1459 // If the attribute type definition includes an X-MIN-VALUE-LENGTH 1460 // extension, then make sure the value is long enough. 1461 final String[] minValueLengths = extensions.get("X-MIN-VALUE-LENGTH"); 1462 if (minValueLengths != null) 1463 { 1464 int minLength = 0; 1465 for (final String s : minValueLengths) 1466 { 1467 try 1468 { 1469 minLength = Math.max(minLength, Integer.parseInt(s)); 1470 } 1471 catch (final Exception e) 1472 { 1473 Debug.debugException(e); 1474 } 1475 } 1476 1477 if (v.stringValue().length() < minLength) 1478 { 1479 entryValid = false; 1480 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1481 if (invalidReasons != null) 1482 { 1483 invalidReasons.add( 1484 ERR_ENTRY_ATTR_VALUE_SHORTER_THAN_MIN_LENGTH.get( 1485 v.stringValue(), d.getNameOrOID(), minLength)); 1486 } 1487 } 1488 } 1489 1490 1491 // If the attribute type definition includes an X-MAX-VALUE-LENGTH 1492 // extension, then make sure the value is short enough. 1493 final String[] maxValueLengths = extensions.get("X-MAX-VALUE-LENGTH"); 1494 if (maxValueLengths != null) 1495 { 1496 int maxLength = Integer.MAX_VALUE; 1497 for (final String s : maxValueLengths) 1498 { 1499 try 1500 { 1501 maxLength = Math.min(maxLength, Integer.parseInt(s)); 1502 } 1503 catch (final Exception e) 1504 { 1505 Debug.debugException(e); 1506 } 1507 } 1508 1509 if (v.stringValue().length() > maxLength) 1510 { 1511 entryValid = false; 1512 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1513 if (invalidReasons != null) 1514 { 1515 invalidReasons.add( 1516 ERR_ENTRY_ATTR_VALUE_LONGER_THAN_MAX_LENGTH.get( 1517 v.stringValue(), d.getNameOrOID(), maxLength)); 1518 } 1519 } 1520 } 1521 1522 1523 // If the attribute type definition includes an X-MIN-INT-VALUE 1524 // extension, then make sure the value is large enough. 1525 final String[] minIntValues = extensions.get("X-MIN-INT-VALUE"); 1526 if (minIntValues != null) 1527 { 1528 try 1529 { 1530 final long longValue = Long.parseLong(v.stringValue()); 1531 1532 long minAllowedValue = 0L; 1533 for (final String s : minIntValues) 1534 { 1535 try 1536 { 1537 minAllowedValue = 1538 Math.max(minAllowedValue, Long.parseLong(s)); 1539 } 1540 catch (final Exception e) 1541 { 1542 Debug.debugException(e); 1543 } 1544 } 1545 1546 if (longValue < minAllowedValue) 1547 { 1548 entryValid = false; 1549 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1550 if (invalidReasons != null) 1551 { 1552 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_INT_TOO_SMALL.get( 1553 longValue, d.getNameOrOID(), minAllowedValue)); 1554 } 1555 } 1556 } 1557 catch (final Exception e) 1558 { 1559 Debug.debugException(e); 1560 entryValid = false; 1561 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1562 if (invalidReasons != null) 1563 { 1564 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_INT.get( 1565 v.stringValue(), d.getNameOrOID(), "X-MIN-INT-VALUE")); 1566 } 1567 } 1568 } 1569 1570 1571 // If the attribute type definition includes an X-MAX-INT-VALUE 1572 // extension, then make sure the value is large enough. 1573 final String[] maxIntValues = extensions.get("X-MAX-INT-VALUE"); 1574 if (maxIntValues != null) 1575 { 1576 try 1577 { 1578 final long longValue = Long.parseLong(v.stringValue()); 1579 1580 long maxAllowedValue = Long.MAX_VALUE; 1581 for (final String s : maxIntValues) 1582 { 1583 try 1584 { 1585 maxAllowedValue = 1586 Math.min(maxAllowedValue, Long.parseLong(s)); 1587 } 1588 catch (final Exception e) 1589 { 1590 Debug.debugException(e); 1591 } 1592 } 1593 1594 if (longValue > maxAllowedValue) 1595 { 1596 entryValid = false; 1597 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1598 if (invalidReasons != null) 1599 { 1600 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_INT_TOO_LARGE.get( 1601 longValue, d.getNameOrOID(), maxAllowedValue)); 1602 } 1603 } 1604 } 1605 catch (final Exception e) 1606 { 1607 Debug.debugException(e); 1608 entryValid = false; 1609 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1610 if (invalidReasons != null) 1611 { 1612 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_INT.get( 1613 v.stringValue(), d.getNameOrOID(), "X-MAX-INT-VALUE")); 1614 } 1615 } 1616 } 1617 } 1618 1619 1620 // If the attribute type definition includes an X-MIN-VALUE-COUNT 1621 // extension, then make sure the value has enough values. 1622 final String[] minValueCounts = extensions.get("X-MIN-VALUE-COUNT"); 1623 if (minValueCounts != null) 1624 { 1625 int minValueCount = 0; 1626 for (final String s : minValueCounts) 1627 { 1628 try 1629 { 1630 minValueCount = Math.max(minValueCount, Integer.parseInt(s)); 1631 } 1632 catch (final Exception e) 1633 { 1634 Debug.debugException(e); 1635 } 1636 } 1637 1638 if (rawValues.length < minValueCount) 1639 { 1640 entryValid = false; 1641 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1642 if (invalidReasons != null) 1643 { 1644 invalidReasons.add(ERR_ENTRY_TOO_FEW_VALUES.get(rawValues.length, 1645 d.getNameOrOID(), minValueCount)); 1646 } 1647 } 1648 } 1649 1650 1651 // If the attribute type definition includes an X-MAX-VALUE-COUNT 1652 // extension, then make sure the value has enough values. 1653 final String[] maxValueCounts = extensions.get("X-MAX-VALUE-COUNT"); 1654 if (maxValueCounts != null) 1655 { 1656 int maxValueCount = Integer.MAX_VALUE; 1657 for (final String s : maxValueCounts) 1658 { 1659 try 1660 { 1661 maxValueCount = Math.min(maxValueCount, Integer.parseInt(s)); 1662 } 1663 catch (final Exception e) 1664 { 1665 Debug.debugException(e); 1666 } 1667 } 1668 1669 if (rawValues.length > maxValueCount) 1670 { 1671 entryValid = false; 1672 updateCount(d.getNameOrOID(), attributesViolatingSyntax); 1673 if (invalidReasons != null) 1674 { 1675 invalidReasons.add(ERR_ENTRY_TOO_MANY_VALUES.get(rawValues.length, 1676 d.getNameOrOID(), maxValueCount)); 1677 } 1678 } 1679 } 1680 } 1681 } 1682 1683 return entryValid; 1684 } 1685 1686 1687 1688 /** 1689 * Ensures that all of the auxiliary object classes contained in the object 1690 * class set are allowed by the provided DIT content rule. 1691 * 1692 * @param ocSet The set of object classes contained in the entry. 1693 * @param ditContentRule The DIT content rule to use to make the 1694 * determination. 1695 * @param invalidReasons A list to which messages may be added which provide 1696 * information about why the entry is invalid. It may 1697 * be {@code null} if this information is not needed. 1698 * 1699 * @return {@code true} if the entry passes all checks performed by this 1700 * method, or {@code false} if not. 1701 */ 1702 private boolean checkAuxiliaryClasses( 1703 final HashSet<ObjectClassDefinition> ocSet, 1704 final DITContentRuleDefinition ditContentRule, 1705 final List<String> invalidReasons) 1706 { 1707 final HashSet<ObjectClassDefinition> auxSet = 1708 new HashSet<>(StaticUtils.computeMapCapacity(20)); 1709 for (final String s : ditContentRule.getAuxiliaryClasses()) 1710 { 1711 final ObjectClassDefinition d = schema.getObjectClass(s); 1712 if (d != null) 1713 { 1714 auxSet.add(d); 1715 } 1716 } 1717 1718 boolean entryValid = true; 1719 for (final ObjectClassDefinition d : ocSet) 1720 { 1721 final ObjectClassType t = d.getObjectClassType(schema); 1722 if ((t == ObjectClassType.AUXILIARY) && (! auxSet.contains(d))) 1723 { 1724 entryValid = false; 1725 updateCount(d.getNameOrOID(), prohibitedObjectClasses); 1726 if (invalidReasons != null) 1727 { 1728 invalidReasons.add( 1729 ERR_ENTRY_AUX_CLASS_NOT_ALLOWED.get(d.getNameOrOID())); 1730 } 1731 } 1732 } 1733 1734 return entryValid; 1735 } 1736 1737 1738 1739 /** 1740 * Ensures that the provided RDN is acceptable. It will ensure that all 1741 * attributes are defined in the schema and allowed for the entry, and that 1742 * the entry optionally conforms to the associated name form. 1743 * 1744 * @param rdn The RDN to examine. 1745 * @param entry The entry to examine. 1746 * @param requiredAttrs The set of attribute types which are required to be 1747 * included in the entry. 1748 * @param optionalAttrs The set of attribute types which may optionally be 1749 * included in the entry. 1750 * @param nameForm The name for to use to make the determination, if 1751 * defined. 1752 * @param invalidReasons A list to which messages may be added which provide 1753 * information about why the entry is invalid. It may 1754 * be {@code null} if this information is not needed. 1755 * 1756 * @return {@code true} if the entry passes all checks performed by this 1757 * method, or {@code false} if not. 1758 */ 1759 private boolean checkRDN(final RDN rdn, final Entry entry, 1760 final HashSet<AttributeTypeDefinition> requiredAttrs, 1761 final HashSet<AttributeTypeDefinition> optionalAttrs, 1762 final NameFormDefinition nameForm, 1763 final List<String> invalidReasons) 1764 { 1765 final HashSet<AttributeTypeDefinition> nfReqAttrs = 1766 new HashSet<>(StaticUtils.computeMapCapacity(5)); 1767 final HashSet<AttributeTypeDefinition> nfAllowedAttrs = 1768 new HashSet<>(StaticUtils.computeMapCapacity(5)); 1769 if (nameForm != null) 1770 { 1771 for (final String s : nameForm.getRequiredAttributes()) 1772 { 1773 final AttributeTypeDefinition d = schema.getAttributeType(s); 1774 if (d != null) 1775 { 1776 nfReqAttrs.add(d); 1777 } 1778 } 1779 1780 nfAllowedAttrs.addAll(nfReqAttrs); 1781 for (final String s : nameForm.getOptionalAttributes()) 1782 { 1783 final AttributeTypeDefinition d = schema.getAttributeType(s); 1784 if (d != null) 1785 { 1786 nfAllowedAttrs.add(d); 1787 } 1788 } 1789 } 1790 1791 boolean entryValid = true; 1792 final String[] attributeNames = rdn.getAttributeNames(); 1793 final byte[][] attributeValues = rdn.getByteArrayAttributeValues(); 1794 for (int i=0; i < attributeNames.length; i++) 1795 { 1796 final String name = attributeNames[i]; 1797 if (checkEntryMissingRDNValues) 1798 { 1799 final byte[] value = attributeValues[i]; 1800 final MatchingRule matchingRule = 1801 MatchingRule.selectEqualityMatchingRule(name, schema); 1802 if (! entry.hasAttributeValue(name, value, matchingRule)) 1803 { 1804 entryValid = false; 1805 entriesMissingRDNValues.incrementAndGet(); 1806 if (invalidReasons != null) 1807 { 1808 invalidReasons.add(ERR_ENTRY_MISSING_RDN_VALUE.get( 1809 rdn.getAttributeValues()[i], name)); 1810 } 1811 } 1812 } 1813 1814 final AttributeTypeDefinition d = schema.getAttributeType(name); 1815 if (d == null) 1816 { 1817 if (checkUndefinedAttributes) 1818 { 1819 entryValid = false; 1820 updateCount(name, undefinedAttributes); 1821 if (invalidReasons != null) 1822 { 1823 invalidReasons.add(ERR_ENTRY_RDN_ATTR_NOT_DEFINED.get(name)); 1824 } 1825 } 1826 } 1827 else 1828 { 1829 if (checkProhibitedAttributes && 1830 (! (requiredAttrs.contains(d) || optionalAttrs.contains(d) || 1831 d.isOperational()))) 1832 { 1833 entryValid = false; 1834 updateCount(d.getNameOrOID(), prohibitedAttributes); 1835 if (invalidReasons != null) 1836 { 1837 invalidReasons.add(ERR_ENTRY_RDN_ATTR_NOT_ALLOWED_IN_ENTRY.get( 1838 d.getNameOrOID())); 1839 } 1840 } 1841 1842 if (checkNameForms && (nameForm != null)) 1843 { 1844 if (! nfReqAttrs.remove(d)) 1845 { 1846 if (! nfAllowedAttrs.contains(d)) 1847 { 1848 if (entryValid) 1849 { 1850 entryValid = false; 1851 nameFormViolations.incrementAndGet(); 1852 } 1853 if (invalidReasons != null) 1854 { 1855 invalidReasons.add( 1856 ERR_ENTRY_RDN_ATTR_NOT_ALLOWED_BY_NF.get(name)); 1857 } 1858 } 1859 } 1860 } 1861 } 1862 } 1863 1864 if (checkNameForms && (! nfReqAttrs.isEmpty())) 1865 { 1866 if (entryValid) 1867 { 1868 entryValid = false; 1869 nameFormViolations.incrementAndGet(); 1870 } 1871 if (invalidReasons != null) 1872 { 1873 for (final AttributeTypeDefinition d : nfReqAttrs) 1874 { 1875 invalidReasons.add(ERR_ENTRY_RDN_MISSING_REQUIRED_ATTR.get( 1876 d.getNameOrOID())); 1877 } 1878 } 1879 } 1880 1881 return entryValid; 1882 } 1883 1884 1885 1886 /** 1887 * Updates the count for the given key in the provided map, adding a new key 1888 * with a count of one if necessary. 1889 * 1890 * @param key The key for which the count is to be updated. 1891 * @param map The map in which the update is to be made. 1892 */ 1893 private static void updateCount(final String key, 1894 final ConcurrentHashMap<String,AtomicLong> map) 1895 { 1896 final String lowerKey = StaticUtils.toLowerCase(key); 1897 AtomicLong l = map.get(lowerKey); 1898 if (l == null) 1899 { 1900 l = map.putIfAbsent(lowerKey, new AtomicLong(1L)); 1901 if (l == null) 1902 { 1903 return; 1904 } 1905 } 1906 1907 l.incrementAndGet(); 1908 } 1909 1910 1911 1912 /** 1913 * Resets all counts maintained by this entry validator. 1914 */ 1915 public void resetCounts() 1916 { 1917 entriesExamined.set(0L); 1918 entriesMissingRDNValues.set(0L); 1919 invalidEntries.set(0L); 1920 malformedDNs.set(0L); 1921 missingSuperiorClasses.set(0L); 1922 multipleStructuralClasses.set(0L); 1923 nameFormViolations.set(0L); 1924 noObjectClasses.set(0L); 1925 noStructuralClass.set(0L); 1926 1927 attributesViolatingSyntax.clear(); 1928 missingAttributes.clear(); 1929 prohibitedAttributes.clear(); 1930 prohibitedObjectClasses.clear(); 1931 singleValueViolations.clear(); 1932 undefinedAttributes.clear(); 1933 undefinedObjectClasses.clear(); 1934 } 1935 1936 1937 1938 /** 1939 * Retrieves the total number of entries examined during processing. 1940 * 1941 * @return The total number of entries examined during processing. 1942 */ 1943 public long getEntriesExamined() 1944 { 1945 return entriesExamined.get(); 1946 } 1947 1948 1949 1950 /** 1951 * Retrieves the total number of invalid entries encountered during 1952 * processing. 1953 * 1954 * @return The total number of invalid entries encountered during processing. 1955 */ 1956 public long getInvalidEntries() 1957 { 1958 return invalidEntries.get(); 1959 } 1960 1961 1962 1963 /** 1964 * Retrieves the total number of entries examined that had malformed DNs which 1965 * could not be parsed. 1966 * 1967 * @return The total number of entries examined that had malformed DNs. 1968 */ 1969 public long getMalformedDNs() 1970 { 1971 return malformedDNs.get(); 1972 } 1973 1974 1975 1976 /** 1977 * Retrieves the total number of entries examined that included an attribute 1978 * value in the RDN that was not present in the entry attributes. 1979 * 1980 * @return The total number of entries examined that included an attribute 1981 * value in the RDN that was not present in the entry attributes. 1982 */ 1983 public long getEntriesMissingRDNValues() 1984 { 1985 return entriesMissingRDNValues.get(); 1986 } 1987 1988 1989 1990 /** 1991 * Retrieves the total number of entries examined which did not contain any 1992 * object classes. 1993 * 1994 * @return The total number of entries examined which did not contain any 1995 * object classes. 1996 */ 1997 public long getEntriesWithoutAnyObjectClasses() 1998 { 1999 return noObjectClasses.get(); 2000 } 2001 2002 2003 2004 /** 2005 * Retrieves the total number of entries examined which did not contain any 2006 * structural object class. 2007 * 2008 * @return The total number of entries examined which did not contain any 2009 * structural object class. 2010 */ 2011 public long getEntriesMissingStructuralObjectClass() 2012 { 2013 return noStructuralClass.get(); 2014 } 2015 2016 2017 2018 /** 2019 * Retrieves the total number of entries examined which contained more than 2020 * one structural object class. 2021 * 2022 * @return The total number of entries examined which contained more than one 2023 * structural object class. 2024 */ 2025 public long getEntriesWithMultipleStructuralObjectClasses() 2026 { 2027 return multipleStructuralClasses.get(); 2028 } 2029 2030 2031 2032 /** 2033 * Retrieves the total number of entries examined which were missing one or 2034 * more superior object classes. 2035 * 2036 * @return The total number of entries examined which were missing one or 2037 * more superior object classes. 2038 */ 2039 public long getEntriesWithMissingSuperiorObjectClasses() 2040 { 2041 return missingSuperiorClasses.get(); 2042 } 2043 2044 2045 2046 /** 2047 * Retrieves the total number of entries examined which contained an RDN that 2048 * violated the constraints of the associated name form. 2049 * 2050 * @return The total number of entries examined which contained an RDN that 2051 * violated the constraints of the associated name form. 2052 */ 2053 public long getNameFormViolations() 2054 { 2055 return nameFormViolations.get(); 2056 } 2057 2058 2059 2060 /** 2061 * Retrieves the total number of undefined object classes encountered while 2062 * examining entries. Note that this number may be greater than the total 2063 * number of entries examined if entries contain multiple undefined object 2064 * classes. 2065 * 2066 * @return The total number of undefined object classes encountered while 2067 * examining entries. 2068 */ 2069 public long getTotalUndefinedObjectClasses() 2070 { 2071 return getMapTotal(undefinedObjectClasses); 2072 } 2073 2074 2075 2076 /** 2077 * Retrieves the undefined object classes encountered while processing 2078 * entries, mapped from the name of the undefined object class to the number 2079 * of entries in which that object class was referenced. 2080 * 2081 * @return The undefined object classes encountered while processing entries. 2082 */ 2083 public Map<String,Long> getUndefinedObjectClasses() 2084 { 2085 return convertMap(undefinedObjectClasses); 2086 } 2087 2088 2089 2090 /** 2091 * Retrieves the total number of undefined attribute types encountered while 2092 * examining entries. Note that this number may be greater than the total 2093 * number of entries examined if entries contain multiple undefined attribute 2094 * types. 2095 * 2096 * @return The total number of undefined attribute types encountered while 2097 * examining entries. 2098 */ 2099 public long getTotalUndefinedAttributes() 2100 { 2101 return getMapTotal(undefinedAttributes); 2102 } 2103 2104 2105 2106 /** 2107 * Retrieves the undefined attribute types encountered while processing 2108 * entries, mapped from the name of the undefined attribute to the number 2109 * of entries in which that attribute type was referenced. 2110 * 2111 * @return The undefined attribute types encountered while processing 2112 * entries. 2113 */ 2114 public Map<String,Long> getUndefinedAttributes() 2115 { 2116 return convertMap(undefinedAttributes); 2117 } 2118 2119 2120 2121 /** 2122 * Retrieves the total number of prohibited object classes encountered while 2123 * examining entries. Note that this number may be greater than the total 2124 * number of entries examined if entries contain multiple prohibited object 2125 * classes. 2126 * 2127 * @return The total number of prohibited object classes encountered while 2128 * examining entries. 2129 */ 2130 public long getTotalProhibitedObjectClasses() 2131 { 2132 return getMapTotal(prohibitedObjectClasses); 2133 } 2134 2135 2136 2137 /** 2138 * Retrieves the prohibited object classes encountered while processing 2139 * entries, mapped from the name of the object class to the number of entries 2140 * in which that object class was referenced. 2141 * 2142 * @return The prohibited object classes encountered while processing 2143 * entries. 2144 */ 2145 public Map<String,Long> getProhibitedObjectClasses() 2146 { 2147 return convertMap(prohibitedObjectClasses); 2148 } 2149 2150 2151 2152 /** 2153 * Retrieves the total number of prohibited attributes encountered while 2154 * examining entries. Note that this number may be greater than the total 2155 * number of entries examined if entries contain multiple prohibited 2156 * attributes. 2157 * 2158 * @return The total number of prohibited attributes encountered while 2159 * examining entries. 2160 */ 2161 public long getTotalProhibitedAttributes() 2162 { 2163 return getMapTotal(prohibitedAttributes); 2164 } 2165 2166 2167 2168 /** 2169 * Retrieves the prohibited attributes encountered while processing entries, 2170 * mapped from the name of the attribute to the number of entries in which 2171 * that attribute was referenced. 2172 * 2173 * @return The prohibited attributes encountered while processing entries. 2174 */ 2175 public Map<String,Long> getProhibitedAttributes() 2176 { 2177 return convertMap(prohibitedAttributes); 2178 } 2179 2180 2181 2182 /** 2183 * Retrieves the total number of missing required attributes encountered while 2184 * examining entries. Note that this number may be greater than the total 2185 * number of entries examined if entries are missing multiple attributes. 2186 * 2187 * @return The total number of missing required attributes encountered while 2188 * examining entries. 2189 */ 2190 public long getTotalMissingAttributes() 2191 { 2192 return getMapTotal(missingAttributes); 2193 } 2194 2195 2196 2197 /** 2198 * Retrieves the missing required encountered while processing entries, mapped 2199 * from the name of the attribute to the number of entries in which that 2200 * attribute was required but not found. 2201 * 2202 * @return The prohibited attributes encountered while processing entries. 2203 */ 2204 public Map<String,Long> getMissingAttributes() 2205 { 2206 return convertMap(missingAttributes); 2207 } 2208 2209 2210 2211 /** 2212 * Retrieves the total number of attribute values which violate their 2213 * associated syntax that were encountered while examining entries. Note that 2214 * this number may be greater than the total number of entries examined if 2215 * entries contain multiple malformed attribute values. 2216 * 2217 * @return The total number of attribute values which violate their 2218 * associated syntax that were encountered while examining entries. 2219 */ 2220 public long getTotalAttributesViolatingSyntax() 2221 { 2222 return getMapTotal(attributesViolatingSyntax); 2223 } 2224 2225 2226 2227 /** 2228 * Retrieves the attributes with values violating their associated syntax that 2229 * were encountered while processing entries, mapped from the name of the 2230 * attribute to the number of malformed values found for that attribute. 2231 * 2232 * @return The attributes with malformed values encountered while processing 2233 * entries. 2234 */ 2235 public Map<String,Long> getAttributesViolatingSyntax() 2236 { 2237 return convertMap(attributesViolatingSyntax); 2238 } 2239 2240 2241 2242 /** 2243 * Retrieves the total number of attributes defined as single-valued that 2244 * contained multiple values which were encountered while processing entries. 2245 * Note that this number may be greater than the total number of entries 2246 * examined if entries contain multiple such attributes. 2247 * 2248 * @return The total number of attribute defined as single-valued that 2249 * contained multiple values which were encountered while processing 2250 * entries. 2251 */ 2252 public long getTotalSingleValueViolations() 2253 { 2254 return getMapTotal(singleValueViolations); 2255 } 2256 2257 2258 2259 /** 2260 * Retrieves the attributes defined as single-valued that contained multiple 2261 * values which were encountered while processing entries, mapped from the 2262 * name of the attribute to the number of entries in which that attribute had 2263 * multiple values. 2264 * 2265 * @return The attributes defined as single-valued that contained multiple 2266 * values which were encountered while processing entries. 2267 */ 2268 public Map<String,Long> getSingleValueViolations() 2269 { 2270 return convertMap(singleValueViolations); 2271 } 2272 2273 2274 2275 /** 2276 * Retrieves the total number of occurrences for all items in the provided 2277 * map. 2278 * 2279 * @param map The map to be processed. 2280 * 2281 * @return The total number of occurrences for all items in the provided map. 2282 */ 2283 private static long getMapTotal(final Map<String,AtomicLong> map) 2284 { 2285 long total = 0L; 2286 2287 for (final AtomicLong l : map.values()) 2288 { 2289 total += l.longValue(); 2290 } 2291 2292 return total; 2293 } 2294 2295 2296 2297 /** 2298 * Converts the provided map from strings to atomic longs to a map from 2299 * strings to longs. 2300 * 2301 * @param map The map to be processed. 2302 * 2303 * @return The new map. 2304 */ 2305 private static Map<String,Long> convertMap(final Map<String,AtomicLong> map) 2306 { 2307 final TreeMap<String,Long> m = new TreeMap<>(); 2308 for (final Map.Entry<String,AtomicLong> e : map.entrySet()) 2309 { 2310 m.put(e.getKey(), e.getValue().longValue()); 2311 } 2312 2313 return Collections.unmodifiableMap(m); 2314 } 2315 2316 2317 2318 /** 2319 * Retrieves a list of messages providing a summary of the invalid entries 2320 * processed by this class. 2321 * 2322 * @param detailedResults Indicates whether to include detailed information 2323 * about the attributes and object classes 2324 * responsible for the violations. 2325 * 2326 * @return A list of messages providing a summary of the invalid entries 2327 * processed by this class, or an empty list if all entries examined 2328 * were valid. 2329 */ 2330 public List<String> getInvalidEntrySummary(final boolean detailedResults) 2331 { 2332 final long numInvalid = invalidEntries.get(); 2333 if (numInvalid == 0) 2334 { 2335 return Collections.emptyList(); 2336 } 2337 2338 final ArrayList<String> messages = new ArrayList<>(5); 2339 final long numEntries = entriesExamined.get(); 2340 long pct = 100 * numInvalid / numEntries; 2341 messages.add(INFO_ENTRY_INVALID_ENTRY_COUNT.get( 2342 numInvalid, numEntries, pct)); 2343 2344 final long numBadDNs = malformedDNs.get(); 2345 if (numBadDNs > 0) 2346 { 2347 pct = 100 * numBadDNs / numEntries; 2348 messages.add(INFO_ENTRY_MALFORMED_DN_COUNT.get( 2349 numBadDNs, numEntries, pct)); 2350 } 2351 2352 final long numEntriesMissingRDNValues = entriesMissingRDNValues.get(); 2353 if (numEntriesMissingRDNValues > 0) 2354 { 2355 pct = 100* numEntriesMissingRDNValues / numEntries; 2356 messages.add(INFO_ENTRY_MISSING_RDN_VALUE_COUNT.get( 2357 numEntriesMissingRDNValues, numEntries, pct)); 2358 } 2359 2360 final long numNoOCs = noObjectClasses.get(); 2361 if (numNoOCs > 0) 2362 { 2363 pct = 100 * numNoOCs / numEntries; 2364 messages.add(INFO_ENTRY_NO_OC_COUNT.get(numNoOCs, numEntries, pct)); 2365 } 2366 2367 final long numMissingStructural = noStructuralClass.get(); 2368 if (numMissingStructural > 0) 2369 { 2370 pct = 100 * numMissingStructural / numEntries; 2371 messages.add(INFO_ENTRY_NO_STRUCTURAL_OC_COUNT.get( 2372 numMissingStructural, numEntries, pct)); 2373 } 2374 2375 final long numMultipleStructural = multipleStructuralClasses.get(); 2376 if (numMultipleStructural > 0) 2377 { 2378 pct = 100 * numMultipleStructural / numEntries; 2379 messages.add(INFO_ENTRY_MULTIPLE_STRUCTURAL_OCS_COUNT.get( 2380 numMultipleStructural, numEntries, pct)); 2381 } 2382 2383 final long numNFViolations = nameFormViolations.get(); 2384 if (numNFViolations > 0) 2385 { 2386 pct = 100 * numNFViolations / numEntries; 2387 messages.add(INFO_ENTRY_NF_VIOLATION_COUNT.get( 2388 numNFViolations, numEntries, pct)); 2389 } 2390 2391 final long numUndefinedOCs = getTotalUndefinedObjectClasses(); 2392 if (numUndefinedOCs > 0) 2393 { 2394 messages.add(INFO_ENTRY_UNDEFINED_OC_COUNT.get(numUndefinedOCs)); 2395 if (detailedResults) 2396 { 2397 for (final Map.Entry<String,AtomicLong> e : 2398 undefinedObjectClasses.entrySet()) 2399 { 2400 messages.add(INFO_ENTRY_UNDEFINED_OC_NAME_COUNT.get( 2401 e.getKey(), e.getValue().longValue())); 2402 } 2403 } 2404 } 2405 2406 final long numProhibitedOCs = getTotalProhibitedObjectClasses(); 2407 if (numProhibitedOCs > 0) 2408 { 2409 messages.add(INFO_ENTRY_PROHIBITED_OC_COUNT.get(numProhibitedOCs)); 2410 if (detailedResults) 2411 { 2412 for (final Map.Entry<String,AtomicLong> e : 2413 prohibitedObjectClasses.entrySet()) 2414 { 2415 messages.add(INFO_ENTRY_PROHIBITED_OC_NAME_COUNT.get( 2416 e.getKey(), e.getValue().longValue())); 2417 } 2418 } 2419 } 2420 2421 final long numMissingSuperior = 2422 getEntriesWithMissingSuperiorObjectClasses(); 2423 if (numMissingSuperior > 0) 2424 { 2425 messages.add( 2426 INFO_ENTRY_MISSING_SUPERIOR_OC_COUNT.get(numMissingSuperior)); 2427 } 2428 2429 final long numUndefinedAttrs = getTotalUndefinedAttributes(); 2430 if (numUndefinedAttrs > 0) 2431 { 2432 messages.add(INFO_ENTRY_UNDEFINED_ATTR_COUNT.get(numUndefinedAttrs)); 2433 if (detailedResults) 2434 { 2435 for (final Map.Entry<String,AtomicLong> e : 2436 undefinedAttributes.entrySet()) 2437 { 2438 messages.add(INFO_ENTRY_UNDEFINED_ATTR_NAME_COUNT.get( 2439 e.getKey(), e.getValue().longValue())); 2440 } 2441 } 2442 } 2443 2444 final long numMissingAttrs = getTotalMissingAttributes(); 2445 if (numMissingAttrs > 0) 2446 { 2447 messages.add(INFO_ENTRY_MISSING_ATTR_COUNT.get(numMissingAttrs)); 2448 if (detailedResults) 2449 { 2450 for (final Map.Entry<String,AtomicLong> e : 2451 missingAttributes.entrySet()) 2452 { 2453 messages.add(INFO_ENTRY_MISSING_ATTR_NAME_COUNT.get( 2454 e.getKey(), e.getValue().longValue())); 2455 } 2456 } 2457 } 2458 2459 final long numProhibitedAttrs = getTotalProhibitedAttributes(); 2460 if (numProhibitedAttrs > 0) 2461 { 2462 messages.add(INFO_ENTRY_PROHIBITED_ATTR_COUNT.get(numProhibitedAttrs)); 2463 if (detailedResults) 2464 { 2465 for (final Map.Entry<String,AtomicLong> e : 2466 prohibitedAttributes.entrySet()) 2467 { 2468 messages.add(INFO_ENTRY_PROHIBITED_ATTR_NAME_COUNT.get( 2469 e.getKey(), e.getValue().longValue())); 2470 } 2471 } 2472 } 2473 2474 final long numSingleValuedViolations = getTotalSingleValueViolations(); 2475 if (numSingleValuedViolations > 0) 2476 { 2477 messages.add(INFO_ENTRY_SINGLE_VALUE_VIOLATION_COUNT.get( 2478 numSingleValuedViolations)); 2479 if (detailedResults) 2480 { 2481 for (final Map.Entry<String,AtomicLong> e : 2482 singleValueViolations.entrySet()) 2483 { 2484 messages.add(INFO_ENTRY_SINGLE_VALUE_VIOLATION_NAME_COUNT.get( 2485 e.getKey(), e.getValue().longValue())); 2486 } 2487 } 2488 } 2489 2490 final long numSyntaxViolations = getTotalAttributesViolatingSyntax(); 2491 if (numSyntaxViolations > 0) 2492 { 2493 messages.add(INFO_ENTRY_SYNTAX_VIOLATION_COUNT.get(numSyntaxViolations)); 2494 if (detailedResults) 2495 { 2496 for (final Map.Entry<String,AtomicLong> e : 2497 attributesViolatingSyntax.entrySet()) 2498 { 2499 messages.add(INFO_ENTRY_SYNTAX_VIOLATION_NAME_COUNT.get( 2500 e.getKey(), e.getValue().longValue())); 2501 } 2502 } 2503 } 2504 2505 return Collections.unmodifiableList(messages); 2506 } 2507}