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