opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Entries.java
@@ -23,18 +23,22 @@ * * * Copyright 2010 Sun Microsystems, Inc. * Portions copyright 2011 ForgeRock AS */ package org.forgerock.opendj.ldap; import java.util.Collection; import java.util.Iterator; import static org.forgerock.opendj.ldap.AttributeDescription.objectClass; import java.util.*; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizedIllegalArgumentException; import org.forgerock.opendj.ldap.requests.ModifyRequest; import org.forgerock.opendj.ldap.requests.Requests; import org.forgerock.opendj.ldap.schema.*; import com.forgerock.opendj.util.Function; import com.forgerock.opendj.util.Iterables; @@ -352,6 +356,79 @@ /** * Returns {@code true} if the provided entry is valid according to the * specified schema and schema validation policy. * <p> * If attribute value validation is enabled then following checks will be * performed: * <ul> * <li>checking that there is at least one value * <li>checking that single-valued attributes contain only a single value * </ul> * In particular, attribute values will not be checked for conformance to * their syntax since this is expected to have already been performed while * adding the values to the entry. * * @param entry * The entry to be validated. * @param schema * The schema against which the entry will be validated. * @param policy * The schema validation policy. * @param errorMessages * A collection into which any schema validation warnings or error * messages can be placed, or {@code null} if they should not be * saved. * @return {@code true} if the provided entry is valid according to the * specified schema and schema validation policy. * @see Schema#validateEntry(Entry, SchemaValidationPolicy, Collection) */ public static boolean conformsToSchema(final Entry entry, final Schema schema, final SchemaValidationPolicy policy, final Collection<LocalizableMessage> errorMessages) { return schema.validateEntry(entry, policy, errorMessages); } /** * Returns {@code true} if the provided entry is valid according to the * default schema and schema validation policy. * <p> * If attribute value validation is enabled then following checks will be * performed: * <ul> * <li>checking that there is at least one value * <li>checking that single-valued attributes contain only a single value * </ul> * In particular, attribute values will not be checked for conformance to * their syntax since this is expected to have already been performed while * adding the values to the entry. * * @param entry * The entry to be validated. * @param policy * The schema validation policy. * @param errorMessages * A collection into which any schema validation warnings or error * messages can be placed, or {@code null} if they should not be * saved. * @return {@code true} if the provided entry is valid according to the * default schema and schema validation policy. * @see Schema#validateEntry(Entry, SchemaValidationPolicy, Collection) */ public static boolean conformsToSchema(final Entry entry, final SchemaValidationPolicy policy, final Collection<LocalizableMessage> errorMessages) { return conformsToSchema(entry, Schema.getDefaultSchema(), policy, errorMessages); } /** * Creates a new modify request containing a list of modifications which can * be used to transform {@code fromEntry} into entry {@code toEntry}. * <p> @@ -379,7 +456,7 @@ * If {@code fromEntry} or {@code toEntry} were {@code null}. * @see Requests#newModifyRequest(Entry, Entry) */ public static final ModifyRequest diffEntries(final Entry fromEntry, public static ModifyRequest diffEntries(final Entry fromEntry, final Entry toEntry) throws NullPointerException { Validator.ensureNotNull(fromEntry, toEntry); @@ -488,6 +565,146 @@ /** * Returns an unmodifiable set containing the object classes associated with * the provided entry. This method will ignore unrecognized object classes. * <p> * This method uses the default schema for decoding the object class attribute * values. * * @param entry * The entry whose object classes are required. * @return An unmodifiable set containing the object classes associated with * the provided entry. */ public static Set<ObjectClass> getObjectClasses(final Entry entry) { return getObjectClasses(entry, Schema.getDefaultSchema()); } /** * Returns an unmodifiable set containing the object classes associated with * the provided entry. This method will ignore unrecognized object classes. * * @param entry * The entry whose object classes are required. * @param schema * The schema which should be used for decoding the object class * attribute values. * @return An unmodifiable set containing the object classes associated with * the provided entry. */ public static Set<ObjectClass> getObjectClasses(final Entry entry, final Schema schema) { final Attribute objectClassAttribute = entry .getAttribute(AttributeDescription.objectClass()); if (objectClassAttribute == null) { return Collections.emptySet(); } else { final Set<ObjectClass> objectClasses = new HashSet<ObjectClass>( objectClassAttribute.size()); for (final ByteString v : objectClassAttribute) { final String objectClassName = v.toString(); final ObjectClass objectClass; try { objectClass = schema.getObjectClass(objectClassName); objectClasses.add(objectClass); } catch (final UnknownSchemaElementException e) { // Ignore. continue; } } return Collections.unmodifiableSet(objectClasses); } } /** * Returns the structural object class associated with the provided entry, or * {@code null} if none was found. If the entry contains multiple structural * object classes then the first will be returned. This method will ignore * unrecognized object classes. * <p> * This method uses the default schema for decoding the object class attribute * values. * * @param entry * The entry whose structural object class is required. * @return The structural object class associated with the provided entry, or * {@code null} if none was found. */ public static ObjectClass getStructuralObjectClass(final Entry entry) { return getStructuralObjectClass(entry, Schema.getDefaultSchema()); } /** * Returns the structural object class associated with the provided entry, or * {@code null} if none was found. If the entry contains multiple structural * object classes then the first will be returned. This method will ignore * unrecognized object classes. * * @param entry * The entry whose structural object class is required. * @param schema * The schema which should be used for decoding the object class * attribute values. * @return The structural object class associated with the provided entry, or * {@code null} if none was found. */ public static ObjectClass getStructuralObjectClass(final Entry entry, final Schema schema) { ObjectClass structuralObjectClass = null; final Attribute objectClassAttribute = entry.getAttribute(objectClass()); if (objectClassAttribute == null) { return null; } for (final ByteString v : objectClassAttribute) { final String objectClassName = v.toString(); final ObjectClass objectClass; try { objectClass = schema.getObjectClass(objectClassName); } catch (final UnknownSchemaElementException e) { // Ignore. continue; } if (objectClass.getObjectClassType() == ObjectClassType.STRUCTURAL) { if (structuralObjectClass == null || objectClass.isDescendantOf(structuralObjectClass)) { structuralObjectClass = objectClass; } } } return structuralObjectClass; } /** * Returns a read-only view of {@code entry} and its attributes. Query * operations on the returned entry and its attributes "read-through" to the * underlying entry or attribute, and attempts to modify the returned entry @@ -500,7 +717,7 @@ * @throws NullPointerException * If {@code entry} was {@code null}. */ public static final Entry unmodifiableEntry(final Entry entry) public static Entry unmodifiableEntry(final Entry entry) throws NullPointerException { return new UnmodifiableEntry(entry); opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java
@@ -23,6 +23,7 @@ * * * Copyright 2009 Sun Microsystems, Inc. * Portions copyright 2011 ForgeRock AS */ package org.forgerock.opendj.ldap.schema; @@ -42,7 +43,7 @@ /** * This class defines a DIT content rule, which defines the set of allowed, * required, and prohibited attributes for entries with a given structural * objectclass, and also indicates which auxiliary classes that may be included * objectclass, and also indicates which auxiliary classes may be included * in the entry. */ public final class DITContentRule extends SchemaElement @@ -287,6 +288,55 @@ /** * Indicates whether the provided attribute type is included in the optional * attribute list for this DIT content rule. * * @param attributeType * The attribute type for which to make the determination. * @return <code>true</code> if the provided attribute type is optional for * this DIT content rule, or <code>false</code> if not. */ public boolean isOptional(final AttributeType attributeType) { return optionalAttributes.contains(attributeType); } /** * Indicates whether the provided attribute type is included in the required * attribute list for this DIT content rule. * * @param attributeType * The attribute type for which to make the determination. * @return <code>true</code> if the provided attribute type is required by * this DIT content rule, or <code>false</code> if not. */ public boolean isRequired(final AttributeType attributeType) { return requiredAttributes.contains(attributeType); } /** * Indicates whether the provided attribute type is in the list of required or * optional attributes for this DIT content rule. * * @param attributeType * The attribute type for which to make the determination. * @return <code>true</code> if the provided attribute type is required or * allowed for this DIT content rule, or <code>false</code> if it is * not. */ public boolean isRequiredOrOptional(final AttributeType attributeType) { return isRequired(attributeType) || isOptional(attributeType); } /** * Returns the string representation of this schema definition in the form * specified in RFC 2252. * opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringSyntaxImpl.java
@@ -109,9 +109,7 @@ public boolean valueIsAcceptable(final Schema schema, final ByteSequence value, final LocalizableMessageBuilder invalidReason) { if (value.length() > 0 || schema.getSchemaCompatOptions() .allowZeroLengthDirectoryStrings()) if (value.length() > 0 || schema.allowZeroLengthDirectoryStrings()) { return true; } opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java
@@ -23,6 +23,7 @@ * * * Copyright 2009 Sun Microsystems, Inc. * Portions copyright 2011 ForgeRock AS */ package org.forgerock.opendj.ldap.schema; @@ -250,6 +251,55 @@ /** * Indicates whether the provided attribute type is included in the optional * attribute list for this name form. * * @param attributeType * The attribute type for which to make the determination. * @return <code>true</code> if the provided attribute type is optional for * this name form, or <code>false</code> if not. */ public boolean isOptional(final AttributeType attributeType) { return optionalAttributes.contains(attributeType); } /** * Indicates whether the provided attribute type is included in the required * attribute list for this name form. * * @param attributeType * The attribute type for which to make the determination. * @return <code>true</code> if the provided attribute type is required by * this name form, or <code>false</code> if not. */ public boolean isRequired(final AttributeType attributeType) { return requiredAttributes.contains(attributeType); } /** * Indicates whether the provided attribute type is in the list of required or * optional attributes for this name form. * * @param attributeType * The attribute type for which to make the determination. * @return <code>true</code> if the provided attribute type is required or * allowed for this name form, or <code>false</code> if it is * not. */ public boolean isRequiredOrOptional(final AttributeType attributeType) { return isRequired(attributeType) || isOptional(attributeType); } /** * Returns the string representation of this schema definition in the form * specified in RFC 2252. * opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
@@ -29,12 +29,10 @@ import static org.forgerock.opendj.ldap.AttributeDescription.objectClass; import static org.forgerock.opendj.ldap.CoreMessages.*; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.*; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.opendj.ldap.*; @@ -63,17 +61,36 @@ { private static final class EmptyImpl implements Impl { private final SchemaCompatOptions options; private EmptyImpl() { this.options = SchemaCompatOptions.defaultOptions(); // Nothing to do. } public boolean allowMalformedNamesAndOptions() { return true; } public boolean allowNonStandardTelephoneNumbers() { return true; } public boolean allowZeroLengthDirectoryStrings() { return false; } @Override public AttributeType getAttributeType(final String name) { // Construct an placeholder attribute type with the given name, @@ -91,6 +108,7 @@ @Override public Collection<AttributeType> getAttributeTypes() { return Collections.emptyList(); @@ -98,13 +116,23 @@ public List<AttributeType> getAttributeTypesByName(final String name) @Override public List<AttributeType> getAttributeTypesWithName(final String name) { return Collections.emptyList(); } @Override public DITContentRule getDITContentRule(final ObjectClass structuralClass) { return null; } @Override public DITContentRule getDITContentRule(final String name) throws UnknownSchemaElementException { @@ -113,6 +141,7 @@ @Override public Collection<DITContentRule> getDITContentRules() { return Collections.emptyList(); @@ -120,13 +149,16 @@ public Collection<DITContentRule> getDITContentRulesByName(final String name) @Override public Collection<DITContentRule> getDITContentRulesWithName( final String name) { return Collections.emptyList(); } @Override public DITStructureRule getDITStructureRule(final int ruleID) throws UnknownSchemaElementException { @@ -136,15 +168,8 @@ public Collection<DITStructureRule> getDITStructureRulesByName( final String name) { return Collections.emptyList(); } public Collection<DITStructureRule> getDITStructureRulesByNameForm( @Override public Collection<DITStructureRule> getDITStructureRules( final NameForm nameForm) { return Collections.emptyList(); @@ -152,6 +177,16 @@ @Override public Collection<DITStructureRule> getDITStructureRulesWithName( final String name) { return Collections.emptyList(); } @Override public Collection<DITStructureRule> getDITStuctureRules() { return Collections.emptyList(); @@ -159,6 +194,7 @@ @Override public MatchingRule getMatchingRule(final String name) throws UnknownSchemaElementException { @@ -167,6 +203,7 @@ @Override public Collection<MatchingRule> getMatchingRules() { return Collections.emptyList(); @@ -174,21 +211,23 @@ public Collection<MatchingRule> getMatchingRulesByName(final String name) @Override public Collection<MatchingRule> getMatchingRulesWithName(final String name) { return Collections.emptyList(); } @Override public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) throws UnknownSchemaElementException { return getMatchingRuleUse(matchingRule.getOID()); return null; } @Override public MatchingRuleUse getMatchingRuleUse(final String name) throws UnknownSchemaElementException { @@ -197,6 +236,7 @@ @Override public Collection<MatchingRuleUse> getMatchingRuleUses() { return Collections.emptyList(); @@ -204,7 +244,8 @@ public Collection<MatchingRuleUse> getMatchingRuleUsesByName( @Override public Collection<MatchingRuleUse> getMatchingRuleUsesWithName( final String name) { return Collections.emptyList(); @@ -212,6 +253,7 @@ @Override public NameForm getNameForm(final String name) throws UnknownSchemaElementException { @@ -220,14 +262,7 @@ public Collection<NameForm> getNameFormByObjectClass( final ObjectClass structuralClass) { return Collections.emptyList(); } @Override public Collection<NameForm> getNameForms() { return Collections.emptyList(); @@ -235,22 +270,33 @@ public Collection<NameForm> getNameFormsByName(final String name) @Override public Collection<NameForm> getNameForms(final ObjectClass structuralClass) { return Collections.emptyList(); } public ObjectClass getObjectClass(final String name) throws UnknownSchemaElementException @Override public Collection<NameForm> getNameFormsWithName(final String name) { throw new UnknownSchemaElementException(WARN_OBJECTCLASS_UNKNOWN .get(name)); return Collections.emptyList(); } @Override public ObjectClass getObjectClass(final String name) throws UnknownSchemaElementException { throw new UnknownSchemaElementException( WARN_OBJECTCLASS_UNKNOWN.get(name)); } @Override public Collection<ObjectClass> getObjectClasses() { return Collections.emptyList(); @@ -258,23 +304,18 @@ public Collection<ObjectClass> getObjectClassesByName(final String name) @Override public Collection<ObjectClass> getObjectClassesWithName(final String name) { return Collections.emptyList(); } public SchemaCompatOptions getSchemaCompatOptions() { return options; } /** * {@inheritDoc} */ @Override public String getSchemaName() { return "Empty Schema"; @@ -282,6 +323,7 @@ @Override public Syntax getSyntax(final String numericOID) { // Fake up a syntax substituted by the default syntax. @@ -290,6 +332,7 @@ @Override public Collection<Syntax> getSyntaxes() { return Collections.emptyList(); @@ -297,6 +340,7 @@ @Override public Collection<LocalizableMessage> getWarnings() { return Collections.emptyList(); @@ -304,6 +348,7 @@ @Override public boolean hasAttributeType(final String name) { // In theory a non-strict schema always contains the requested @@ -315,6 +360,7 @@ @Override public boolean hasDITContentRule(final String name) { return false; @@ -322,6 +368,7 @@ @Override public boolean hasDITStructureRule(final int ruleID) { return false; @@ -329,6 +376,7 @@ @Override public boolean hasMatchingRule(final String name) { return false; @@ -336,6 +384,7 @@ @Override public boolean hasMatchingRuleUse(final String name) { return false; @@ -343,6 +392,7 @@ @Override public boolean hasNameForm(final String name) { return false; @@ -350,6 +400,7 @@ @Override public boolean hasObjectClass(final String name) { return false; @@ -357,6 +408,7 @@ @Override public boolean hasSyntax(final String numericOID) { return false; @@ -364,6 +416,7 @@ @Override public boolean isStrict() { return false; @@ -383,7 +436,11 @@ List<AttributeType> getAttributeTypesByName(String name); List<AttributeType> getAttributeTypesWithName(String name); DITContentRule getDITContentRule(ObjectClass structuralClass); @@ -396,7 +453,7 @@ Collection<DITContentRule> getDITContentRulesByName(String name); Collection<DITContentRule> getDITContentRulesWithName(String name); @@ -405,12 +462,11 @@ Collection<DITStructureRule> getDITStructureRulesByName(String name); Collection<DITStructureRule> getDITStructureRules(NameForm nameForm); Collection<DITStructureRule> getDITStructureRulesByNameForm( NameForm nameForm); Collection<DITStructureRule> getDITStructureRulesWithName(String name); @@ -427,12 +483,11 @@ Collection<MatchingRule> getMatchingRulesByName(String name); Collection<MatchingRule> getMatchingRulesWithName(String name); MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule) throws UnknownSchemaElementException; MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule); @@ -445,7 +500,7 @@ Collection<MatchingRuleUse> getMatchingRuleUsesByName(String name); Collection<MatchingRuleUse> getMatchingRuleUsesWithName(String name); @@ -453,15 +508,15 @@ Collection<NameForm> getNameFormByObjectClass(ObjectClass structuralClass); Collection<NameForm> getNameForms(); Collection<NameForm> getNameFormsByName(String name); Collection<NameForm> getNameForms(ObjectClass structuralClass); Collection<NameForm> getNameFormsWithName(String name); @@ -474,11 +529,7 @@ Collection<ObjectClass> getObjectClassesByName(String name); SchemaCompatOptions getSchemaCompatOptions(); Collection<ObjectClass> getObjectClassesWithName(String name); @@ -531,6 +582,18 @@ boolean isStrict(); boolean allowMalformedNamesAndOptions(); boolean allowNonStandardTelephoneNumbers(); boolean allowZeroLengthDirectoryStrings(); } @@ -548,6 +611,28 @@ public boolean allowMalformedNamesAndOptions() { return strictImpl.allowMalformedNamesAndOptions(); } public boolean allowNonStandardTelephoneNumbers() { return strictImpl.allowNonStandardTelephoneNumbers(); } public boolean allowZeroLengthDirectoryStrings() { return strictImpl.allowZeroLengthDirectoryStrings(); } @Override public AttributeType getAttributeType(final String name) throws UnknownSchemaElementException { @@ -570,6 +655,7 @@ @Override public Collection<AttributeType> getAttributeTypes() { return strictImpl.getAttributeTypes(); @@ -577,13 +663,23 @@ public List<AttributeType> getAttributeTypesByName(final String name) @Override public List<AttributeType> getAttributeTypesWithName(final String name) { return strictImpl.getAttributeTypesByName(name); return strictImpl.getAttributeTypesWithName(name); } @Override public DITContentRule getDITContentRule(final ObjectClass structuralClass) { return strictImpl.getDITContentRule(structuralClass); } @Override public DITContentRule getDITContentRule(final String name) throws UnknownSchemaElementException { @@ -592,6 +688,7 @@ @Override public Collection<DITContentRule> getDITContentRules() { return strictImpl.getDITContentRules(); @@ -599,13 +696,16 @@ public Collection<DITContentRule> getDITContentRulesByName(final String name) @Override public Collection<DITContentRule> getDITContentRulesWithName( final String name) { return strictImpl.getDITContentRulesByName(name); return strictImpl.getDITContentRulesWithName(name); } @Override public DITStructureRule getDITStructureRule(final int ruleID) throws UnknownSchemaElementException { @@ -614,22 +714,25 @@ public Collection<DITStructureRule> getDITStructureRulesByName( final String name) { return strictImpl.getDITStructureRulesByName(name); } public Collection<DITStructureRule> getDITStructureRulesByNameForm( @Override public Collection<DITStructureRule> getDITStructureRules( final NameForm nameForm) { return strictImpl.getDITStructureRulesByNameForm(nameForm); return strictImpl.getDITStructureRules(nameForm); } @Override public Collection<DITStructureRule> getDITStructureRulesWithName( final String name) { return strictImpl.getDITStructureRulesWithName(name); } @Override public Collection<DITStructureRule> getDITStuctureRules() { return strictImpl.getDITStuctureRules(); @@ -637,6 +740,7 @@ @Override public MatchingRule getMatchingRule(final String name) throws UnknownSchemaElementException { @@ -645,6 +749,7 @@ @Override public Collection<MatchingRule> getMatchingRules() { return strictImpl.getMatchingRules(); @@ -652,21 +757,23 @@ public Collection<MatchingRule> getMatchingRulesByName(final String name) @Override public Collection<MatchingRule> getMatchingRulesWithName(final String name) { return strictImpl.getMatchingRulesByName(name); return strictImpl.getMatchingRulesWithName(name); } @Override public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) throws UnknownSchemaElementException { return strictImpl.getMatchingRuleUse(matchingRule); } @Override public MatchingRuleUse getMatchingRuleUse(final String name) throws UnknownSchemaElementException { @@ -675,6 +782,7 @@ @Override public Collection<MatchingRuleUse> getMatchingRuleUses() { return strictImpl.getMatchingRuleUses(); @@ -682,14 +790,16 @@ public Collection<MatchingRuleUse> getMatchingRuleUsesByName( @Override public Collection<MatchingRuleUse> getMatchingRuleUsesWithName( final String name) { return strictImpl.getMatchingRuleUsesByName(name); return strictImpl.getMatchingRuleUsesWithName(name); } @Override public NameForm getNameForm(final String name) throws UnknownSchemaElementException { @@ -698,14 +808,7 @@ public Collection<NameForm> getNameFormByObjectClass( final ObjectClass structuralClass) { return strictImpl.getNameFormByObjectClass(structuralClass); } @Override public Collection<NameForm> getNameForms() { return strictImpl.getNameForms(); @@ -713,13 +816,23 @@ public Collection<NameForm> getNameFormsByName(final String name) @Override public Collection<NameForm> getNameForms(final ObjectClass structuralClass) { return strictImpl.getNameFormsByName(name); return strictImpl.getNameForms(structuralClass); } @Override public Collection<NameForm> getNameFormsWithName(final String name) { return strictImpl.getNameFormsWithName(name); } @Override public ObjectClass getObjectClass(final String name) throws UnknownSchemaElementException { @@ -728,6 +841,7 @@ @Override public Collection<ObjectClass> getObjectClasses() { return strictImpl.getObjectClasses(); @@ -735,20 +849,15 @@ public Collection<ObjectClass> getObjectClassesByName(final String name) @Override public Collection<ObjectClass> getObjectClassesWithName(final String name) { return strictImpl.getObjectClassesByName(name); return strictImpl.getObjectClassesWithName(name); } public SchemaCompatOptions getSchemaCompatOptions() { return strictImpl.getSchemaCompatOptions(); } @Override public String getSchemaName() { return strictImpl.getSchemaName(); @@ -756,6 +865,7 @@ @Override public Syntax getSyntax(final String numericOID) { if (!strictImpl.hasSyntax(numericOID)) @@ -767,6 +877,7 @@ @Override public Collection<Syntax> getSyntaxes() { return strictImpl.getSyntaxes(); @@ -774,6 +885,7 @@ @Override public Collection<LocalizableMessage> getWarnings() { return strictImpl.getWarnings(); @@ -781,6 +893,7 @@ @Override public boolean hasAttributeType(final String name) { // In theory a non-strict schema always contains the requested @@ -792,6 +905,7 @@ @Override public boolean hasDITContentRule(final String name) { return strictImpl.hasDITContentRule(name); @@ -799,6 +913,7 @@ @Override public boolean hasDITStructureRule(final int ruleID) { return strictImpl.hasDITStructureRule(ruleID); @@ -806,6 +921,7 @@ @Override public boolean hasMatchingRule(final String name) { return strictImpl.hasMatchingRule(name); @@ -813,6 +929,7 @@ @Override public boolean hasMatchingRuleUse(final String name) { return strictImpl.hasMatchingRuleUse(name); @@ -820,6 +937,7 @@ @Override public boolean hasNameForm(final String name) { return strictImpl.hasNameForm(name); @@ -827,6 +945,7 @@ @Override public boolean hasObjectClass(final String name) { return strictImpl.hasObjectClass(name); @@ -834,6 +953,7 @@ @Override public boolean hasSyntax(final String numericOID) { return strictImpl.hasSyntax(numericOID); @@ -841,6 +961,7 @@ @Override public boolean isStrict() { return false; @@ -885,15 +1006,22 @@ private final Map<String, List<NameForm>> objectClass2NameForms; private final SchemaCompatOptions options; private final List<LocalizableMessage> warnings; private final String schemaName; private final boolean allowNonStandardTelephoneNumbers; private final boolean allowZeroLengthDirectoryStrings; private final boolean allowMalformedNamesAndOptions; private StrictImpl(final String schemaName, StrictImpl(final String schemaName, final boolean allowMalformedNamesAndOptions, final boolean allowNonStandardTelephoneNumbers, final boolean allowZeroLengthDirectoryStrings, final Map<String, Syntax> numericOID2Syntaxes, final Map<String, MatchingRule> numericOID2MatchingRules, final Map<String, MatchingRuleUse> numericOID2MatchingRuleUses, @@ -911,10 +1039,12 @@ final Map<String, List<DITStructureRule>> name2StructureRules, final Map<String, List<NameForm>> objectClass2NameForms, final Map<String, List<DITStructureRule>> nameForm2StructureRules, final SchemaCompatOptions options, final List<LocalizableMessage> warnings) { this.schemaName = schemaName; this.allowMalformedNamesAndOptions = allowMalformedNamesAndOptions; this.allowNonStandardTelephoneNumbers = allowNonStandardTelephoneNumbers; this.allowZeroLengthDirectoryStrings = allowZeroLengthDirectoryStrings; this.numericOID2Syntaxes = Collections .unmodifiableMap(numericOID2Syntaxes); this.numericOID2MatchingRules = Collections @@ -944,12 +1074,33 @@ .unmodifiableMap(objectClass2NameForms); this.nameForm2StructureRules = Collections .unmodifiableMap(nameForm2StructureRules); this.options = options; this.warnings = Collections.unmodifiableList(warnings); } public boolean allowMalformedNamesAndOptions() { return allowMalformedNamesAndOptions; } public boolean allowNonStandardTelephoneNumbers() { return allowNonStandardTelephoneNumbers; } public boolean allowZeroLengthDirectoryStrings() { return allowZeroLengthDirectoryStrings; } @Override public AttributeType getAttributeType(final String name) throws UnknownSchemaElementException { @@ -966,14 +1117,15 @@ { return attributes.get(0); } throw new UnknownSchemaElementException(WARN_ATTR_TYPE_AMBIGIOUS .get(name)); throw new UnknownSchemaElementException( WARN_ATTR_TYPE_AMBIGIOUS.get(name)); } throw new UnknownSchemaElementException(WARN_ATTR_TYPE_UNKNOWN.get(name)); } @Override public Collection<AttributeType> getAttributeTypes() { return numericOID2AttributeTypes.values(); @@ -981,7 +1133,8 @@ public List<AttributeType> getAttributeTypesByName(final String name) @Override public List<AttributeType> getAttributeTypesWithName(final String name) { final List<AttributeType> attributes = name2AttributeTypes .get(StaticUtils.toLowerCase(name)); @@ -997,6 +1150,15 @@ @Override public DITContentRule getDITContentRule(final ObjectClass structuralClass) { return numericOID2ContentRules.get(structuralClass.getOID()); } @Override public DITContentRule getDITContentRule(final String name) throws UnknownSchemaElementException { @@ -1020,6 +1182,7 @@ @Override public Collection<DITContentRule> getDITContentRules() { return numericOID2ContentRules.values(); @@ -1027,7 +1190,9 @@ public Collection<DITContentRule> getDITContentRulesByName(final String name) @Override public Collection<DITContentRule> getDITContentRulesWithName( final String name) { final List<DITContentRule> rules = name2ContentRules.get(StaticUtils .toLowerCase(name)); @@ -1043,6 +1208,7 @@ @Override public DITStructureRule getDITStructureRule(final int ruleID) throws UnknownSchemaElementException { @@ -1057,24 +1223,8 @@ public Collection<DITStructureRule> getDITStructureRulesByName( final String name) { final List<DITStructureRule> rules = name2StructureRules.get(StaticUtils .toLowerCase(name)); if (rules == null) { return Collections.emptyList(); } else { return rules; } } public Collection<DITStructureRule> getDITStructureRulesByNameForm( @Override public Collection<DITStructureRule> getDITStructureRules( final NameForm nameForm) { final List<DITStructureRule> rules = nameForm2StructureRules.get(nameForm @@ -1091,6 +1241,25 @@ @Override public Collection<DITStructureRule> getDITStructureRulesWithName( final String name) { final List<DITStructureRule> rules = name2StructureRules.get(StaticUtils .toLowerCase(name)); if (rules == null) { return Collections.emptyList(); } else { return rules; } } @Override public Collection<DITStructureRule> getDITStuctureRules() { return id2StructureRules.values(); @@ -1098,6 +1267,7 @@ @Override public MatchingRule getMatchingRule(final String name) throws UnknownSchemaElementException { @@ -1121,6 +1291,7 @@ @Override public Collection<MatchingRule> getMatchingRules() { return numericOID2MatchingRules.values(); @@ -1128,7 +1299,8 @@ public Collection<MatchingRule> getMatchingRulesByName(final String name) @Override public Collection<MatchingRule> getMatchingRulesWithName(final String name) { final List<MatchingRule> rules = name2MatchingRules.get(StaticUtils .toLowerCase(name)); @@ -1144,14 +1316,15 @@ @Override public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) throws UnknownSchemaElementException { return getMatchingRuleUse(matchingRule.getOID()); return numericOID2MatchingRuleUses.get(matchingRule.getOID()); } @Override public MatchingRuleUse getMatchingRuleUse(final String name) throws UnknownSchemaElementException { @@ -1175,6 +1348,7 @@ @Override public Collection<MatchingRuleUse> getMatchingRuleUses() { return numericOID2MatchingRuleUses.values(); @@ -1182,7 +1356,8 @@ public Collection<MatchingRuleUse> getMatchingRuleUsesByName( @Override public Collection<MatchingRuleUse> getMatchingRuleUsesWithName( final String name) { final List<MatchingRuleUse> rules = name2MatchingRuleUses.get(StaticUtils @@ -1199,6 +1374,7 @@ @Override public NameForm getNameForm(final String name) throws UnknownSchemaElementException { @@ -1215,16 +1391,24 @@ { return forms.get(0); } throw new UnknownSchemaElementException(WARN_NAMEFORM_AMBIGIOUS .get(name)); throw new UnknownSchemaElementException( WARN_NAMEFORM_AMBIGIOUS.get(name)); } throw new UnknownSchemaElementException(WARN_NAMEFORM_UNKNOWN.get(name)); } public Collection<NameForm> getNameFormByObjectClass( final ObjectClass structuralClass) @Override public Collection<NameForm> getNameForms() { return numericOID2NameForms.values(); } @Override public Collection<NameForm> getNameForms(final ObjectClass structuralClass) { final List<NameForm> forms = objectClass2NameForms.get(structuralClass .getOID()); @@ -1240,14 +1424,8 @@ public Collection<NameForm> getNameForms() { return numericOID2NameForms.values(); } public Collection<NameForm> getNameFormsByName(final String name) @Override public Collection<NameForm> getNameFormsWithName(final String name) { final List<NameForm> forms = name2NameForms.get(StaticUtils .toLowerCase(name)); @@ -1263,6 +1441,7 @@ @Override public ObjectClass getObjectClass(final String name) throws UnknownSchemaElementException { @@ -1279,15 +1458,16 @@ { return classes.get(0); } throw new UnknownSchemaElementException(WARN_OBJECTCLASS_AMBIGIOUS .get(name)); throw new UnknownSchemaElementException( WARN_OBJECTCLASS_AMBIGIOUS.get(name)); } throw new UnknownSchemaElementException(WARN_OBJECTCLASS_UNKNOWN .get(name)); throw new UnknownSchemaElementException( WARN_OBJECTCLASS_UNKNOWN.get(name)); } @Override public Collection<ObjectClass> getObjectClasses() { return numericOID2ObjectClasses.values(); @@ -1295,7 +1475,8 @@ public Collection<ObjectClass> getObjectClassesByName(final String name) @Override public Collection<ObjectClass> getObjectClassesWithName(final String name) { final List<ObjectClass> classes = name2ObjectClasses.get(StaticUtils .toLowerCase(name)); @@ -1311,13 +1492,7 @@ public SchemaCompatOptions getSchemaCompatOptions() { return options; } @Override public String getSchemaName() { return schemaName; @@ -1325,20 +1500,22 @@ @Override public Syntax getSyntax(final String numericOID) throws UnknownSchemaElementException { final Syntax syntax = numericOID2Syntaxes.get(numericOID); if (syntax == null) { throw new UnknownSchemaElementException(WARN_SYNTAX_UNKNOWN .get(numericOID)); throw new UnknownSchemaElementException( WARN_SYNTAX_UNKNOWN.get(numericOID)); } return syntax; } @Override public Collection<Syntax> getSyntaxes() { return numericOID2Syntaxes.values(); @@ -1346,6 +1523,7 @@ @Override public Collection<LocalizableMessage> getWarnings() { return warnings; @@ -1353,6 +1531,7 @@ @Override public boolean hasAttributeType(final String name) { if (numericOID2AttributeTypes.containsKey(name)) @@ -1366,6 +1545,7 @@ @Override public boolean hasDITContentRule(final String name) { if (numericOID2ContentRules.containsKey(name)) @@ -1379,6 +1559,7 @@ @Override public boolean hasDITStructureRule(final int ruleID) { return id2StructureRules.containsKey(ruleID); @@ -1386,6 +1567,7 @@ @Override public boolean hasMatchingRule(final String name) { if (numericOID2MatchingRules.containsKey(name)) @@ -1399,6 +1581,7 @@ @Override public boolean hasMatchingRuleUse(final String name) { if (numericOID2MatchingRuleUses.containsKey(name)) @@ -1412,6 +1595,7 @@ @Override public boolean hasNameForm(final String name) { if (numericOID2NameForms.containsKey(name)) @@ -1425,6 +1609,7 @@ @Override public boolean hasObjectClass(final String name) { if (numericOID2ObjectClasses.containsKey(name)) @@ -1438,6 +1623,7 @@ @Override public boolean hasSyntax(final String numericOID) { return numericOID2Syntaxes.containsKey(numericOID); @@ -1445,6 +1631,7 @@ @Override public boolean isStrict() { return true; @@ -1452,6 +1639,7 @@ } /* * WARNING: do not reference the core schema in the following declarations. */ @@ -1600,8 +1788,8 @@ * @throws UnsupportedOperationException * If the connection does not support search operations. * @throws IllegalStateException * If the connection has already been closed, i.e. if {@code * connection.isClosed() == true}. * If the connection has already been closed, i.e. if * {@code connection.isClosed() == true}. * @throws NullPointerException * If the {@code connection} or {@code name} was {@code null}. */ @@ -1701,8 +1889,8 @@ * @throws UnsupportedOperationException * If the connection does not support search operations. * @throws IllegalStateException * If the connection has already been closed, i.e. if {@code * connection.isClosed() == true}. * If the connection has already been closed, i.e. if * {@code connection.isClosed() == true}. * @throws NullPointerException * If the {@code connection} or {@code name} was {@code null}. */ @@ -1767,6 +1955,9 @@ Schema(final String schemaName, final boolean allowMalformedNamesAndOptions, final boolean allowNonStandardTelephoneNumbers, final boolean allowZeroLengthDirectoryStrings, final Map<String, Syntax> numericOID2Syntaxes, final Map<String, MatchingRule> numericOID2MatchingRules, final Map<String, MatchingRuleUse> numericOID2MatchingRuleUses, @@ -1784,16 +1975,17 @@ final Map<String, List<DITStructureRule>> name2StructureRules, final Map<String, List<NameForm>> objectClass2NameForms, final Map<String, List<DITStructureRule>> nameForm2StructureRules, final SchemaCompatOptions options, final List<LocalizableMessage> warnings) final List<LocalizableMessage> warnings) { impl = new StrictImpl(schemaName, numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses, numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms, numericOID2ContentRules, id2StructureRules, name2MatchingRules, name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses, name2NameForms, name2ContentRules, name2StructureRules, objectClass2NameForms, nameForm2StructureRules, options, warnings); impl = new StrictImpl(schemaName, allowMalformedNamesAndOptions, allowNonStandardTelephoneNumbers, allowZeroLengthDirectoryStrings, numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses, numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms, numericOID2ContentRules, id2StructureRules, name2MatchingRules, name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses, name2NameForms, name2ContentRules, name2StructureRules, objectClass2NameForms, nameForm2StructureRules, warnings); } @@ -1806,8 +1998,8 @@ /** * Returns {@code true} if this schema allows certain illegal characters * in OIDs and attribute options. When this compatibility option is set to * Returns {@code true} if this schema allows certain illegal characters in * OIDs and attribute options. When this compatibility option is set to * {@code true} the following illegal characters will be permitted in addition * to those permitted in section 1.4 of RFC 4512: * @@ -1819,14 +2011,14 @@ * By default this compatibility option is set to {@code true} because these * characters are often used for naming purposes (such as collation rules). * * @return {@code true} if this schema allows certain illegal characters * in OIDs and attribute options. * @return {@code true} if this schema allows certain illegal characters in * OIDs and attribute options. * @see <a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight * Directory Access Protocol (LDAP): Directory Information Models </a> */ public boolean allowMalformedNamesAndOptions() { return impl.getSchemaCompatOptions().allowMalformedNamesAndOptions(); return impl.allowMalformedNamesAndOptions(); } @@ -1844,8 +2036,7 @@ */ public boolean allowNonStandardTelephoneNumbers() { return impl.getSchemaCompatOptions() .allowNonStandardTelephoneNumbers(); return impl.allowNonStandardTelephoneNumbers(); } @@ -1864,8 +2055,51 @@ */ public boolean allowZeroLengthDirectoryStrings() { return impl.getSchemaCompatOptions() .allowZeroLengthDirectoryStrings(); return impl.allowZeroLengthDirectoryStrings(); } /** * Returns a non-strict view of this schema. * <p> * See the description of {@link #isStrict()} for more details. * * @return A non-strict view of this schema. * @see Schema#isStrict() */ public Schema asNonStrictSchema() { if (impl.isStrict()) { return new Schema(new NonStrictImpl(impl)); } else { return this; } } /** * Returns a strict view of this schema. * <p> * See the description of {@link #isStrict()} for more details. * * @return A strict view of this schema. * @see Schema#isStrict() */ public Schema asStrictSchema() { if (impl.isStrict()) { return this; } else { return new Schema(((NonStrictImpl) impl).strictImpl); } } @@ -1911,9 +2145,25 @@ * @return An unmodifiable collection containing all of the attribute types * having the specified name or numeric OID. */ public List<AttributeType> getAttributeTypesByName(final String name) public List<AttributeType> getAttributeTypesWithName(final String name) { return impl.getAttributeTypesByName(name); return impl.getAttributeTypesWithName(name); } /** * Returns the DIT content rule associated with the provided structural object * class, or {@code null} if no rule is defined. * * @param structuralClass * The structural object class . * @return The DIT content rule associated with the provided structural object * class, or {@code null} if no rule is defined. */ public DITContentRule getDITContentRule(final ObjectClass structuralClass) { return impl.getDITContentRule(structuralClass); } @@ -1959,9 +2209,9 @@ * @return An unmodifiable collection containing all of the DIT content rules * having the specified name or numeric OID. */ public Collection<DITContentRule> getDITContentRulesByName(final String name) public Collection<DITContentRule> getDITContentRulesWithName(final String name) { return impl.getDITContentRulesByName(name); return impl.getDITContentRulesWithName(name); } @@ -1986,6 +2236,23 @@ /** * Returns an unmodifiable collection containing all of the DIT structure * rules associated with the provided name form. * * @param nameForm * The name form. * @return An unmodifiable collection containing all of the DIT structure * rules associated with the provided name form. */ public Collection<DITStructureRule> getDITStructureRules( final NameForm nameForm) { return impl.getDITStructureRules(nameForm); } /** * Returns an unmodifiable collection containing all of the DIT structure * rules having the specified name or numeric OID. * * @param name @@ -1993,25 +2260,10 @@ * @return An unmodifiable collection containing all of the DIT structure * rules having the specified name or numeric OID. */ public Collection<DITStructureRule> getDITStructureRulesByName( public Collection<DITStructureRule> getDITStructureRulesWithName( final String name) { return impl.getDITStructureRulesByName(name); } /** * Retrieves the DIT structure rules for the provided name form. * * @param nameForm * The name form. * @return The requested DIT structure rules. */ public Collection<DITStructureRule> getDITStructureRulesByNameForm( final NameForm nameForm) { return impl.getDITStructureRulesByNameForm(nameForm); return impl.getDITStructureRulesWithName(name); } @@ -2071,25 +2323,23 @@ * @return An unmodifiable collection containing all of the matching rules * having the specified name or numeric OID. */ public Collection<MatchingRule> getMatchingRulesByName(final String name) public Collection<MatchingRule> getMatchingRulesWithName(final String name) { return impl.getMatchingRulesByName(name); return impl.getMatchingRulesWithName(name); } /** * Returns the matching rule use associated with the provided matching rule. * Returns the matching rule use associated with the provided matching rule, * or {@code null} if no use is defined. * * @param matchingRule * The matching rule whose matching rule use is to be retrieved. * @return The requested matching rule use. * @throws UnknownSchemaElementException * If this is a strict schema and the requested matching rule use * was not found or if the provided name is ambiguous. * @return The matching rule use associated with the provided matching rule, * or {@code null} if no use is defined. */ public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) throws UnknownSchemaElementException { return getMatchingRuleUse(matchingRule.getOID()); } @@ -2137,9 +2387,10 @@ * @return An unmodifiable collection containing all of the matching rule uses * having the specified name or numeric OID. */ public Collection<MatchingRuleUse> getMatchingRuleUsesByName(final String name) public Collection<MatchingRuleUse> getMatchingRuleUsesWithName( final String name) { return impl.getMatchingRuleUsesByName(name); return impl.getMatchingRuleUsesWithName(name); } @@ -2163,21 +2414,6 @@ /** * Retrieves the name forms for the specified structural objectclass. * * @param structuralClass * The structural objectclass for the name form to retrieve. * @return The requested name forms */ public Collection<NameForm> getNameFormByObjectClass( final ObjectClass structuralClass) { return impl.getNameFormByObjectClass(structuralClass); } /** * Returns an unmodifiable collection containing all of the name forms * contained in this schema. * @@ -2192,6 +2428,22 @@ /** * Returns an unmodifiable collection containing all of the name forms * associated with the provided structural object class. * * @param structuralClass * The structural object class whose name forms are to be retrieved. * @return An unmodifiable collection containing all of the name forms * associated with the provided structural object class. */ public Collection<NameForm> getNameForms(final ObjectClass structuralClass) { return impl.getNameForms(structuralClass); } /** * Returns an unmodifiable collection containing all of the name forms having * the specified name or numeric OID. * @@ -2200,9 +2452,9 @@ * @return An unmodifiable collection containing all of the name forms having * the specified name or numeric OID. */ public Collection<NameForm> getNameFormsByName(final String name) public Collection<NameForm> getNameFormsWithName(final String name) { return impl.getNameFormsByName(name); return impl.getNameFormsWithName(name); } @@ -2248,9 +2500,9 @@ * @return An unmodifiable collection containing all of the object classes * having the specified name or numeric OID. */ public Collection<ObjectClass> getObjectClassesByName(final String name) public Collection<ObjectClass> getObjectClassesWithName(final String name) { return impl.getObjectClassesByName(name); return impl.getObjectClassesWithName(name); } @@ -2467,50 +2719,6 @@ /** * Returns a non-strict view of this schema. * <p> * See the description of {@link #isStrict()} for more details. * * @return A non-strict view of this schema. * @see Schema#isStrict() */ public Schema asNonStrictSchema() { if (impl.isStrict()) { return new Schema(new NonStrictImpl(impl)); } else { return this; } } /** * Returns a strict view of this schema. * <p> * See the description of {@link #isStrict()} for more details. * * @return A strict view of this schema. * @see Schema#isStrict() */ public Schema asStrictSchema() { if (impl.isStrict()) { return this; } else { return new Schema(((NonStrictImpl) impl).strictImpl); } } /** * Adds the definitions of all the schema elements contained in this schema to * the provided subschema subentry. Any existing attributes (including schema * definitions) contained in the provided entry will be preserved. @@ -2522,10 +2730,10 @@ * @throws NullPointerException * If {@code entry} was {@code null}. */ public Entry toEntry(Entry entry) throws NullPointerException public Entry toEntry(final Entry entry) throws NullPointerException { Attribute attr = new LinkedAttribute(Schema.ATTR_LDAP_SYNTAXES); for (Syntax syntax : getSyntaxes()) for (final Syntax syntax : getSyntaxes()) { attr.add(syntax.toString()); } @@ -2535,7 +2743,7 @@ } attr = new LinkedAttribute(Schema.ATTR_ATTRIBUTE_TYPES); for (AttributeType attributeType : getAttributeTypes()) for (final AttributeType attributeType : getAttributeTypes()) { attr.add(attributeType.toString()); } @@ -2545,7 +2753,7 @@ } attr = new LinkedAttribute(Schema.ATTR_OBJECT_CLASSES); for (ObjectClass objectClass : getObjectClasses()) for (final ObjectClass objectClass : getObjectClasses()) { attr.add(objectClass.toString()); } @@ -2555,7 +2763,7 @@ } attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULE_USE); for (MatchingRuleUse matchingRuleUse : getMatchingRuleUses()) for (final MatchingRuleUse matchingRuleUse : getMatchingRuleUses()) { attr.add(matchingRuleUse.toString()); } @@ -2565,7 +2773,7 @@ } attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULES); for (MatchingRule matchingRule : getMatchingRules()) for (final MatchingRule matchingRule : getMatchingRules()) { attr.add(matchingRule.toString()); } @@ -2575,7 +2783,7 @@ } attr = new LinkedAttribute(Schema.ATTR_DIT_CONTENT_RULES); for (DITContentRule ditContentRule : getDITContentRules()) for (final DITContentRule ditContentRule : getDITContentRules()) { attr.add(ditContentRule.toString()); } @@ -2585,7 +2793,7 @@ } attr = new LinkedAttribute(Schema.ATTR_DIT_STRUCTURE_RULES); for (DITStructureRule ditStructureRule : getDITStuctureRules()) for (final DITStructureRule ditStructureRule : getDITStuctureRules()) { attr.add(ditStructureRule.toString()); } @@ -2595,7 +2803,7 @@ } attr = new LinkedAttribute(Schema.ATTR_NAME_FORMS); for (NameForm nameForm : getNameForms()) for (final NameForm nameForm : getNameForms()) { attr.add(nameForm.toString()); } @@ -2609,8 +2817,631 @@ SchemaCompatOptions getSchemaCompatOptions() /** * Returns {@code true} if the provided entry is valid according to this * schema and the specified schema validation policy. * <p> * If attribute value validation is enabled then following checks will be * performed: * <ul> * <li>checking that there is at least one value * <li>checking that single-valued attributes contain only a single value * </ul> * In particular, attribute values will not be checked for conformance to * their syntax since this is expected to have already been performed while * adding the values to the entry. * * @param entry * The entry to be validated. * @param policy * The schema validation policy. * @param errorMessages * A collection into which any schema validation warnings or error * messages can be placed, or {@code null} if they should not be * saved. * @return {@code true} if an entry conforms to this schema based on the * provided schema validation policy. */ public boolean validateEntry(final Entry entry, final SchemaValidationPolicy policy, final Collection<LocalizableMessage> errorMessages) { return impl.getSchemaCompatOptions(); // First check that the object classes are recognized and that there is one // structural object class. ObjectClass structuralObjectClass = null; final Attribute objectClassAttribute = entry.getAttribute(objectClass()); final List<ObjectClass> objectClasses = new LinkedList<ObjectClass>(); if (objectClassAttribute != null) { for (final ByteString v : objectClassAttribute) { final String objectClassName = v.toString(); final ObjectClass objectClass; try { objectClass = getObjectClass(objectClassName); objectClasses.add(objectClass); } catch (final UnknownSchemaElementException e) { if (policy.checkAttributesAndObjectClasses().needsChecking()) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_UNKNOWN_OBJECT_CLASS .get(entry.getName().toString(), objectClassName); errorMessages.add(message); } if (policy.checkAttributesAndObjectClasses().isReject()) { return false; } } continue; } if (objectClass.getObjectClassType() == ObjectClassType.STRUCTURAL) { if (structuralObjectClass == null || objectClass.isDescendantOf(structuralObjectClass)) { structuralObjectClass = objectClass; } else if (!structuralObjectClass.isDescendantOf(objectClass)) { if (policy.requireSingleStructuralObjectClass().needsChecking()) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_MULTIPLE_STRUCTURAL_CLASSES .get(entry.getName().toString(), structuralObjectClass.getNameOrOID(), objectClassName); errorMessages.add(message); } if (policy.requireSingleStructuralObjectClass().isReject()) { return false; } } } } } } Collection<DITStructureRule> ditStructureRules = Collections.emptyList(); DITContentRule ditContentRule = null; if (structuralObjectClass == null) { if (policy.requireSingleStructuralObjectClass().needsChecking()) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_NO_STRUCTURAL_CLASS .get(entry.getName().toString()); errorMessages.add(message); } if (policy.requireSingleStructuralObjectClass().isReject()) { return false; } } } else { ditContentRule = getDITContentRule(structuralObjectClass); if (ditContentRule != null && ditContentRule.isObsolete()) { ditContentRule = null; } } // Check entry conforms to object classes and optional content rule. if (!checkAttributesAndObjectClasses(entry, policy, errorMessages, objectClasses, ditContentRule)) { return false; } // Check that the name of the entry conforms to at least one applicable name // form. if (policy.checkNameForms().needsChecking() && structuralObjectClass != null) { /** * There may be multiple name forms registered with this structural object * class. However, we need to select only one of the name forms and its * corresponding DIT structure rule(s). We will iterate over all the name * forms and see if at least one is acceptable before rejecting the entry. * DIT structure rules corresponding to other non-acceptable name forms * are not applied. */ boolean foundMatchingNameForms = false; NameForm nameForm = null; final List<LocalizableMessage> nameFormWarnings = (errorMessages != null) ? new LinkedList<LocalizableMessage>() : null; for (final NameForm nf : getNameForms(structuralObjectClass)) { if (nf.isObsolete()) { continue; } // If there are any candidate name forms then at least one should be // valid. foundMatchingNameForms = true; if (checkNameForm(entry, policy, nameFormWarnings, nf)) { nameForm = nf; break; } } if (foundMatchingNameForms) { if (nameForm != null) { ditStructureRules = getDITStructureRules(nameForm); } else { // We couldn't match this entry against any of the name forms, so // append the reasons why they didn't match and reject if required. if (errorMessages != null) { errorMessages.addAll(nameFormWarnings); } if (policy.checkNameForms().isReject()) { return false; } } } } // Check DIT structure rules - this needs the parent entry. if (policy.checkDITStructureRules().needsChecking() && !entry.getName().isRootDN()) { boolean foundMatchingRules = false; boolean foundValidRule = false; final List<LocalizableMessage> ruleWarnings = (errorMessages != null) ? new LinkedList<LocalizableMessage>() : null; ObjectClass parentStructuralObjectClass = null; boolean parentEntryHasBeenRead = false; for (final DITStructureRule rule : ditStructureRules) { if (rule.isObsolete()) { continue; } foundMatchingRules = true; // A DIT structure rule with no superiors is automatically valid, so // avoid reading the parent. if (rule.getSuperiorRules().isEmpty()) { foundValidRule = true; break; } if (!parentEntryHasBeenRead) { // Don't drop out immediately on failure because there may be some // applicable rules which do not require the parent entry. parentStructuralObjectClass = getParentStructuralObjectClass(entry, policy, ruleWarnings); parentEntryHasBeenRead = true; } if (parentStructuralObjectClass != null) { if (checkDITStructureRule(entry, policy, ruleWarnings, rule, structuralObjectClass, parentStructuralObjectClass)) { foundValidRule = true; break; } } } if (foundMatchingRules) { if (!foundValidRule) { // We couldn't match this entry against any of the rules, so // append the reasons why they didn't match and reject if required. if (errorMessages != null) { errorMessages.addAll(ruleWarnings); } if (policy.checkDITStructureRules().isReject()) { return false; } } } else { // There is no DIT structure rule for this entry, but there may // be one for the parent entry. If there is such a rule for the // parent entry, then this entry will not be valid. // The parent won't have been read yet. parentStructuralObjectClass = getParentStructuralObjectClass(entry, policy, ruleWarnings); if (parentStructuralObjectClass == null) { if (errorMessages != null) { errorMessages.addAll(ruleWarnings); } if (policy.checkDITStructureRules().isReject()) { return false; } } else { for (final NameForm nf : getNameForms(parentStructuralObjectClass)) { if (!nf.isObsolete()) { for (final DITStructureRule rule : getDITStructureRules(nf)) { if (!rule.isObsolete()) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_DSR_MISSING_DSR .get(entry.getName().toString(), rule.getNameOrRuleID()); errorMessages.add(message); } if (policy.checkDITStructureRules().isReject()) { return false; } // We could break out of the loop here in warn mode but // continuing allows us to collect all conflicts. } } } } } } } // If we've gotten here, then the entry is acceptable. return true; } private boolean checkAttributesAndObjectClasses(final Entry entry, final SchemaValidationPolicy policy, final Collection<LocalizableMessage> errorMessages, final List<ObjectClass> objectClasses, final DITContentRule ditContentRule) { // Check object classes. final boolean checkDITContentRule = policy.checkDITContentRules() .needsChecking() && ditContentRule != null; final boolean checkObjectClasses = policy.checkAttributesAndObjectClasses() .needsChecking(); final boolean checkAttributeValues = policy.checkAttributeValues() .needsChecking(); if (checkObjectClasses || checkDITContentRule) { for (final ObjectClass objectClass : objectClasses) { // Make sure that any auxiliary object classes are permitted by the // content rule. if (checkDITContentRule) { if (objectClass.getObjectClassType() == ObjectClassType.AUXILIARY) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_DCR_PROHIBITED_AUXILIARY_OC .get(entry.getName().toString(), objectClass.getNameOrOID(), ditContentRule.getNameOrOID()); errorMessages.add(message); } if (policy.checkDITContentRules().isReject()) { return false; } } } // Make sure that all of the attributes required by the object class are // present. if (checkObjectClasses) { for (final AttributeType t : objectClass.getRequiredAttributes()) { final Attribute a = Attributes.emptyAttribute(AttributeDescription .create(t)); if (entry.containsAttribute(a, null)) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_OC_MISSING_MUST_ATTRIBUTES .get(entry.getName().toString(), t.getNameOrOID(), objectClass.getNameOrOID()); errorMessages.add(message); } if (policy.checkAttributesAndObjectClasses().isReject()) { return false; } } } } } // Make sure that all of the attributes required by the content rule are // present. if (checkDITContentRule) { for (final AttributeType t : ditContentRule.getRequiredAttributes()) { final Attribute a = Attributes.emptyAttribute(AttributeDescription .create(t)); if (entry.containsAttribute(a, null)) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_DCR_MISSING_MUST_ATTRIBUTES .get(entry.getName().toString(), t.getNameOrOID(), ditContentRule.getNameOrOID()); errorMessages.add(message); } if (policy.checkDITContentRules().isReject()) { return false; } } } // Make sure that attributes prohibited by the content rule are not // present. for (final AttributeType t : ditContentRule.getProhibitedAttributes()) { final Attribute a = Attributes.emptyAttribute(AttributeDescription .create(t)); if (entry.containsAttribute(a, null)) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_DCR_PROHIBITED_ATTRIBUTES .get(entry.getName().toString(), t.getNameOrOID(), ditContentRule.getNameOrOID()); errorMessages.add(message); } if (policy.checkDITContentRules().isReject()) { return false; } } } } } // Check attributes. if (checkObjectClasses || checkDITContentRule || checkAttributeValues) { for (final Attribute attribute : entry.getAllAttributes()) { final AttributeType t = attribute.getAttributeDescription() .getAttributeType(); if (!t.isOperational()) { if (checkObjectClasses || checkDITContentRule) { boolean isAllowed = false; for (final ObjectClass objectClass : objectClasses) { if (objectClass.isRequiredOrOptional(t)) { isAllowed = true; break; } } if (!isAllowed && ditContentRule != null) { if (ditContentRule.isRequiredOrOptional(t)) { isAllowed = true; } } if (!isAllowed) { if (errorMessages != null) { final LocalizableMessage message; if (ditContentRule == null) { message = ERR_ENTRY_SCHEMA_OC_DISALLOWED_ATTRIBUTES.get(entry .getName().toString(), t.getNameOrOID()); } else { message = ERR_ENTRY_SCHEMA_DCR_DISALLOWED_ATTRIBUTES.get( entry.getName().toString(), t.getNameOrOID(), ditContentRule.getNameOrOID()); } errorMessages.add(message); } if (policy.checkAttributesAndObjectClasses().isReject() || policy.checkDITContentRules().isReject()) { return false; } } } // Check attributes contain an appropriate number of values. if (checkAttributeValues) { final int sz = attribute.size(); if (sz == 0) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_AT_EMPTY_ATTRIBUTE .get(entry.getName().toString(), t.getNameOrOID()); errorMessages.add(message); } if (policy.checkAttributeValues().isReject()) { return false; } } else if (sz > 1 && t.isSingleValue()) { if (errorMessages != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_AT_SINGLE_VALUED_ATTRIBUTE .get(entry.getName().toString(), t.getNameOrOID()); errorMessages.add(message); } if (policy.checkAttributeValues().isReject()) { return false; } } } } } } // If we've gotten here, then things are OK. return true; } private boolean checkDITStructureRule(final Entry entry, final SchemaValidationPolicy policy, final List<LocalizableMessage> ruleWarnings, final DITStructureRule rule, final ObjectClass structuralObjectClass, final ObjectClass parentStructuralObjectClass) { boolean matchFound = false; for (final DITStructureRule parentRule : rule.getSuperiorRules()) { if (parentRule.getNameForm().getStructuralClass() .equals(parentStructuralObjectClass)) { matchFound = true; } } if (!matchFound) { if (ruleWarnings != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_DSR_ILLEGAL_OC.get( entry.getName().toString(), rule.getNameOrRuleID(), structuralObjectClass.getNameOrOID(), parentStructuralObjectClass.getNameOrOID()); ruleWarnings.add(message); } return false; } return true; } private boolean checkNameForm(final Entry entry, final SchemaValidationPolicy policy, final List<LocalizableMessage> nameFormWarnings, final NameForm nameForm) { final RDN rdn = entry.getName().rdn(); if (rdn != null) { // Make sure that all the required AVAs are present. for (final AttributeType t : nameForm.getRequiredAttributes()) { if (rdn.getAttributeValue(t) == null) { if (nameFormWarnings != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_NF_MISSING_MUST_ATTRIBUTES .get(entry.getName().toString(), t.getNameOrOID(), nameForm.getNameOrOID()); nameFormWarnings.add(message); } return false; } } // Make sure that all AVAs in the RDN are allowed. for (final AVA ava : rdn) { final AttributeType t = ava.getAttributeType(); if (nameForm.isRequiredOrOptional(t)) { if (nameFormWarnings != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_NF_DISALLOWED_ATTRIBUTES .get(entry.getName().toString(), t.getNameOrOID(), nameForm.getNameOrOID()); nameFormWarnings.add(message); } return false; } } } // If we've gotten here, then things are OK. return true; } private ObjectClass getParentStructuralObjectClass(final Entry entry, final SchemaValidationPolicy policy, final List<LocalizableMessage> ruleWarnings) { final Entry parentEntry; try { parentEntry = policy.checkDITStructureRulesEntryResolver().getEntry( entry.getName().parent()); } catch (final ErrorResultException e) { if (ruleWarnings != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_DSR_PARENT_NOT_FOUND .get(entry.getName().toString(), e.getResult() .getDiagnosticMessage()); ruleWarnings.add(message); } return null; } final ObjectClass parentStructuralObjectClass = Entries .getStructuralObjectClass(parentEntry, this); if (parentStructuralObjectClass == null) { if (ruleWarnings != null) { final LocalizableMessage message = ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC .get(entry.getName().toString()); ruleWarnings.add(message); } return null; } return parentStructuralObjectClass; } } opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -30,11 +30,17 @@ import static com.forgerock.opendj.util.StaticUtils.toLowerCase; import static org.forgerock.opendj.ldap.CoreMessages.*; import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult; import static org.forgerock.opendj.ldap.schema.Schema.*; import static org.forgerock.opendj.ldap.schema.SchemaConstants.*; import static org.forgerock.opendj.ldap.schema.SchemaUtils.*; import static org.forgerock.opendj.ldap.schema.SchemaConstants.EXTENSIBLE_OBJECT_OBJECTCLASS_OID; import static org.forgerock.opendj.ldap.schema.SchemaConstants.OMR_GENERIC_ENUM_NAME; import static org.forgerock.opendj.ldap.schema.SchemaConstants.SCHEMA_PROPERTY_APPROX_RULE; import static org.forgerock.opendj.ldap.schema.SchemaConstants.TOP_OBJECTCLASS_NAME; import static org.forgerock.opendj.ldap.schema.SchemaUtils.unmodifiableCopyOfExtraProperties; import static org.forgerock.opendj.ldap.schema.SchemaUtils.unmodifiableCopyOfList; import static org.forgerock.opendj.ldap.schema.SchemaUtils.unmodifiableCopyOfSet; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -67,18 +73,7 @@ private static final Filter SUBSCHEMA_FILTER = Filter .valueOf("(objectClass=subschema)"); private static final String[] SUBSCHEMA_SUBENTRY_ATTRS = new String[] { ATTR_SUBSCHEMA_SUBENTRY }; // Constructs a search request for retrieving the named subschema // sub-entry. private static SearchRequest getReadSchemaSearchRequest(final DN dn) { return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, SUBSCHEMA_FILTER, SUBSCHEMA_ATTRS); } private static final String[] SUBSCHEMA_SUBENTRY_ATTRS = new String[] { ATTR_SUBSCHEMA_SUBENTRY }; @@ -92,6 +87,16 @@ // Constructs a search request for retrieving the named subschema // sub-entry. private static SearchRequest getReadSchemaSearchRequest(final DN dn) { return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, SUBSCHEMA_FILTER, SUBSCHEMA_ATTRS); } private static DN getSubschemaSubentryDN(final DN name, final Entry entry) throws ErrorResultException { @@ -156,11 +161,15 @@ private Map<String, List<NameForm>> objectClass2NameForms; private SchemaCompatOptions options; private String schemaName; private List<LocalizableMessage> warnings; private Schema schema; private boolean allowNonStandardTelephoneNumbers; private boolean allowZeroLengthDirectoryStrings; private boolean allowMalformedNamesAndOptions; // A unique ID which can be used to uniquely identify schemas // constructed without a name. @@ -211,7 +220,6 @@ public SchemaBuilder(final Schema schema) throws NullPointerException { initBuilder(schema.getSchemaName()); setSchemaCompatOptions(schema.getSchemaCompatOptions()); addSchema(schema, true); } @@ -287,7 +295,7 @@ // The next set of characters must be the OID. final String oid = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); List<String> names = Collections.emptyList(); String description = "".intern(); @@ -324,7 +332,7 @@ else if (tokenName.equalsIgnoreCase("name")) { names = SchemaUtils.readNameDescriptors(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("desc")) { @@ -346,28 +354,28 @@ // type from which this attribute type should inherit its // properties. superiorType = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("equality")) { // This specifies the name or OID of the equality matching // rule to use for this attribute type. equalityMatchingRule = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("ordering")) { // This specifies the name or OID of the ordering matching // rule to use for this attribute type. orderingMatchingRule = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("substr")) { // This specifies the name or OID of the substring matching // rule to use for this attribute type. substringMatchingRule = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("syntax")) { @@ -379,8 +387,8 @@ // implementation will ignore any such length because it does // not impose any practical limit on the length of attribute // values. syntax = SchemaUtils.readOIDLen(reader, options.allowMalformedNamesAndOptions()); syntax = SchemaUtils .readOIDLen(reader, allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("single-definition")) { @@ -494,8 +502,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_ATTRTYPE_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_ATTRTYPE_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -533,10 +541,10 @@ * be used or, if none is defined, the default matching rule * associated with the syntax. * @param approximateMatchingRule * The OID of the approximate matching rule, which may be {@code * null} indicating that the superior attribute type's matching rule * should be used or, if none is defined, the default matching rule * associated with the syntax. * The OID of the approximate matching rule, which may be * {@code null} indicating that the superior attribute type's * matching rule should be used or, if none is defined, the default * matching rule associated with the syntax. * @param syntax * The OID of the syntax definition. * @param singleValue @@ -638,7 +646,7 @@ // The next set of characters must be the OID. final String structuralClass = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); List<String> names = Collections.emptyList(); String description = "".intern(); @@ -669,7 +677,7 @@ else if (tokenName.equalsIgnoreCase("name")) { names = SchemaUtils.readNameDescriptors(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("desc")) { @@ -688,22 +696,22 @@ else if (tokenName.equalsIgnoreCase("aux")) { auxiliaryClasses = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("must")) { requiredAttributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("may")) { optionalAttributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("not")) { prohibitedAttributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.matches("^X-[A-Za-z_-]+$")) { @@ -738,8 +746,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_DCR_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_DCR_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -933,7 +941,7 @@ else if (tokenName.equalsIgnoreCase("name")) { names = SchemaUtils.readNameDescriptors(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("desc")) { @@ -951,8 +959,7 @@ } else if (tokenName.equalsIgnoreCase("form")) { nameForm = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); nameForm = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("sup")) { @@ -998,8 +1005,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_DSR_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_DSR_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -1031,13 +1038,14 @@ { Validator.ensureNotNull((Object) enumerations); final EnumSyntaxImpl enumImpl = new EnumSyntaxImpl(oid, Arrays .asList(enumerations)); final Syntax enumSyntax = new Syntax(oid, description, Collections .singletonMap("X-ENUM", Arrays.asList(enumerations)), null, enumImpl); final MatchingRule enumOMR = new MatchingRule(enumImpl .getOrderingMatchingRule(), Collections .singletonList(OMR_GENERIC_ENUM_NAME + oid), "", false, oid, final EnumSyntaxImpl enumImpl = new EnumSyntaxImpl(oid, Arrays.asList(enumerations)); final Syntax enumSyntax = new Syntax(oid, description, Collections.singletonMap("X-ENUM", Arrays.asList(enumerations)), null, enumImpl); final MatchingRule enumOMR = new MatchingRule( enumImpl.getOrderingMatchingRule(), Collections.singletonList(OMR_GENERIC_ENUM_NAME + oid), "", false, oid, CoreSchemaImpl.OPENDS_ORIGIN, null, new EnumOrderingMatchingRule( enumImpl)); @@ -1110,7 +1118,7 @@ // The next set of characters must be the OID. final String oid = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); List<String> names = Collections.emptyList(); String description = "".intern(); @@ -1138,7 +1146,7 @@ else if (tokenName.equalsIgnoreCase("name")) { names = SchemaUtils.readNameDescriptors(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("desc")) { @@ -1156,8 +1164,7 @@ } else if (tokenName.equalsIgnoreCase("syntax")) { syntax = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); syntax = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions); } else if (tokenName.matches("^X-[A-Za-z_-]+$")) { @@ -1198,8 +1205,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_MR_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_MR_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -1307,7 +1314,7 @@ // The next set of characters must be the OID. final String oid = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); List<String> names = Collections.emptyList(); String description = "".intern(); @@ -1335,7 +1342,7 @@ else if (tokenName.equalsIgnoreCase("name")) { names = SchemaUtils.readNameDescriptors(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("desc")) { @@ -1354,7 +1361,7 @@ else if (tokenName.equalsIgnoreCase("applies")) { attributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.matches("^X-[A-Za-z_-]+$")) { @@ -1396,8 +1403,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_MRUSE_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_MRUSE_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -1501,7 +1508,7 @@ // The next set of characters must be the OID. final String oid = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); List<String> names = Collections.emptyList(); String description = "".intern(); @@ -1531,7 +1538,7 @@ else if (tokenName.equalsIgnoreCase("name")) { names = SchemaUtils.readNameDescriptors(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("desc")) { @@ -1550,17 +1557,17 @@ else if (tokenName.equalsIgnoreCase("oc")) { structuralClass = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("must")) { requiredAttributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("may")) { optionalAttributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.matches("^X-[A-Za-z_-]+$")) { @@ -1611,8 +1618,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_NAME_FORM_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_NAME_FORM_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -1724,7 +1731,7 @@ // The next set of characters must be the OID. final String oid = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); List<String> names = Collections.emptyList(); String description = "".intern(); @@ -1755,7 +1762,7 @@ else if (tokenName.equalsIgnoreCase("name")) { names = SchemaUtils.readNameDescriptors(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("desc")) { @@ -1774,7 +1781,7 @@ else if (tokenName.equalsIgnoreCase("sup")) { superiorClasses = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("abstract")) { @@ -1799,12 +1806,12 @@ else if (tokenName.equalsIgnoreCase("must")) { requiredAttributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.equalsIgnoreCase("may")) { optionalAttributes = SchemaUtils.readOIDs(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); } else if (tokenName.matches("^X-[A-Za-z_-]+$")) { @@ -1851,8 +1858,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -1958,6 +1965,102 @@ /** * Reads the schema elements contained in the named subschema sub-entry and * adds them to this schema builder. * <p> * If the requested schema is not returned by the Directory Server then the * request will fail with an {@link EntryNotFoundException}. * * @param connection * A connection to the Directory Server whose schema is to be read. * @param name * The distinguished name of the subschema sub-entry. * @param handler * A result handler which can be used to asynchronously process the * operation result when it is received, may be {@code null}. * @param overwrite * {@code true} if existing schema elements with the same conflicting * OIDs should be overwritten. * @return A future representing the updated schema builder. * @throws UnsupportedOperationException * If the connection does not support search operations. * @throws IllegalStateException * If the connection has already been closed, i.e. if * {@code connection.isClosed() == true}. * @throws NullPointerException * If the {@code connection} or {@code name} was {@code null}. */ public FutureResult<SchemaBuilder> addSchema( final AsynchronousConnection connection, final DN name, final ResultHandler<? super SchemaBuilder> handler, final boolean overwrite) throws UnsupportedOperationException, IllegalStateException, NullPointerException { final SearchRequest request = getReadSchemaSearchRequest(name); final FutureResultTransformer<SearchResultEntry, SchemaBuilder> future = new FutureResultTransformer<SearchResultEntry, SchemaBuilder>(handler) { @Override protected SchemaBuilder transformResult(final SearchResultEntry result) throws ErrorResultException { addSchema(result, overwrite); return SchemaBuilder.this; } }; final FutureResult<SearchResultEntry> innerFuture = connection .searchSingleEntry(request, future); future.setFutureResult(innerFuture); return future; } /** * Reads the schema elements contained in the named subschema sub-entry and * adds them to this schema builder. * <p> * If the requested schema is not returned by the Directory Server then the * request will fail with an {@link EntryNotFoundException}. * * @param connection * A connection to the Directory Server whose schema is to be read. * @param name * The distinguished name of the subschema sub-entry. * @param overwrite * {@code true} if existing schema elements with the same conflicting * OIDs should be overwritten. * @return A reference to this schema builder. * @throws ErrorResultException * If the result code indicates that the request failed for some * reason. * @throws InterruptedException * If the current thread was interrupted while waiting. * @throws UnsupportedOperationException * If the connection does not support search operations. * @throws IllegalStateException * If the connection has already been closed, i.e. if * {@code isClosed() == true}. * @throws NullPointerException * If the {@code connection} or {@code name} was {@code null}. */ public SchemaBuilder addSchema(final Connection connection, final DN name, final boolean overwrite) throws ErrorResultException, InterruptedException, UnsupportedOperationException, IllegalStateException, NullPointerException { final SearchRequest request = getReadSchemaSearchRequest(name); final Entry entry = connection.searchSingleEntry(request); return addSchema(entry, overwrite); } /** * Adds all of the schema elements contained in the provided subschema * subentry to this schema builder. Any problems encountered while parsing the * entry can be retrieved using the returned schema's @@ -2176,102 +2279,6 @@ /** * Reads the schema elements contained in the named subschema sub-entry and * adds them to this schema builder. * <p> * If the requested schema is not returned by the Directory Server then the * request will fail with an {@link EntryNotFoundException}. * * @param connection * A connection to the Directory Server whose schema is to be read. * @param name * The distinguished name of the subschema sub-entry. * @param handler * A result handler which can be used to asynchronously process the * operation result when it is received, may be {@code null}. * @param overwrite * {@code true} if existing schema elements with the same conflicting * OIDs should be overwritten. * @return A future representing the updated schema builder. * @throws UnsupportedOperationException * If the connection does not support search operations. * @throws IllegalStateException * If the connection has already been closed, i.e. if * {@code connection.isClosed() == true}. * @throws NullPointerException * If the {@code connection} or {@code name} was {@code null}. */ public FutureResult<SchemaBuilder> addSchema( final AsynchronousConnection connection, final DN name, final ResultHandler<? super SchemaBuilder> handler, final boolean overwrite) throws UnsupportedOperationException, IllegalStateException, NullPointerException { final SearchRequest request = getReadSchemaSearchRequest(name); final FutureResultTransformer<SearchResultEntry, SchemaBuilder> future = new FutureResultTransformer<SearchResultEntry, SchemaBuilder>(handler) { @Override protected SchemaBuilder transformResult(final SearchResultEntry result) throws ErrorResultException { addSchema(result, overwrite); return SchemaBuilder.this; } }; final FutureResult<SearchResultEntry> innerFuture = connection .searchSingleEntry(request, future); future.setFutureResult(innerFuture); return future; } /** * Reads the schema elements contained in the named subschema sub-entry and * adds them to this schema builder. * <p> * If the requested schema is not returned by the Directory Server then the * request will fail with an {@link EntryNotFoundException}. * * @param connection * A connection to the Directory Server whose schema is to be read. * @param name * The distinguished name of the subschema sub-entry. * @param overwrite * {@code true} if existing schema elements with the same conflicting * OIDs should be overwritten. * @return A reference to this schema builder. * @throws ErrorResultException * If the result code indicates that the request failed for some * reason. * @throws InterruptedException * If the current thread was interrupted while waiting. * @throws UnsupportedOperationException * If the connection does not support search operations. * @throws IllegalStateException * If the connection has already been closed, i.e. if * {@code isClosed() == true}. * @throws NullPointerException * If the {@code connection} or {@code name} was {@code null}. */ public SchemaBuilder addSchema(final Connection connection, final DN name, final boolean overwrite) throws ErrorResultException, InterruptedException, UnsupportedOperationException, IllegalStateException, NullPointerException { final SearchRequest request = getReadSchemaSearchRequest(name); final Entry entry = connection.searchSingleEntry(request); return addSchema(entry, overwrite); } /** * Reads the schema elements contained in the subschema sub-entry which * applies to the named entry and adds them to this schema builder. * <p> @@ -2403,8 +2410,10 @@ { Validator.ensureNotNull(substituteSyntax); addSyntax(new Syntax(oid, description, Collections.singletonMap("X-SUBST", Collections.singletonList(substituteSyntax)), null, null), overwrite); addSyntax( new Syntax(oid, description, Collections.singletonMap("X-SUBST", Collections.singletonList(substituteSyntax)), null, null), overwrite); return this; } @@ -2465,7 +2474,7 @@ // The next set of characters must be the OID. final String oid = SchemaUtils.readOID(reader, options.allowMalformedNamesAndOptions()); allowMalformedNamesAndOptions); String description = "".intern(); Map<String, List<String>> extraProperties = Collections.emptyMap(); @@ -2525,15 +2534,15 @@ { if (property.getKey().equalsIgnoreCase("x-enum")) { final EnumSyntaxImpl enumImpl = new EnumSyntaxImpl(oid, property .getValue()); final EnumSyntaxImpl enumImpl = new EnumSyntaxImpl(oid, property.getValue()); final Syntax enumSyntax = new Syntax(oid, description, extraProperties, definition, enumImpl); final MatchingRule enumOMR = new MatchingRule(enumImpl .getOrderingMatchingRule(), Collections .singletonList(OMR_GENERIC_ENUM_NAME + oid), "", false, oid, CoreSchemaImpl.OPENDS_ORIGIN, null, new EnumOrderingMatchingRule( enumImpl)); final MatchingRule enumOMR = new MatchingRule( enumImpl.getOrderingMatchingRule(), Collections.singletonList(OMR_GENERIC_ENUM_NAME + oid), "", false, oid, CoreSchemaImpl.OPENDS_ORIGIN, null, new EnumOrderingMatchingRule(enumImpl)); addSyntax(enumSyntax, overwrite); addMatchingRule(enumOMR, overwrite); @@ -2547,8 +2556,8 @@ } catch (final DecodeException e) { LocalizableMessage msg = ERR_ATTR_SYNTAX_ATTRSYNTAX_INVALID1.get(definition, e.getMessageObject()); final LocalizableMessage msg = ERR_ATTR_SYNTAX_ATTRSYNTAX_INVALID1.get( definition, e.getMessageObject()); throw new LocalizedIllegalArgumentException(msg, e.getCause()); } return this; @@ -2592,6 +2601,80 @@ /** * Specifies whether or not the schema should allow certain illegal characters * in OIDs and attribute options. When this compatibility option is set to * {@code true} the following illegal characters will be permitted in addition * to those permitted in section 1.4 of RFC 4512: * * <pre> * USCORE = %x5F ; underscore ("_") * DOT = %x2E ; period (".") * </pre> * * By default this compatibility option is set to {@code true} because these * characters are often used for naming purposes (such as collation rules). * * @param allowMalformedNamesAndOptions * {@code true} if the schema should allow certain illegal characters * in OIDs and attribute options. * @return A reference to this {@code SchemaBuilder}. * @see <a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight * Directory Access Protocol (LDAP): Directory Information Models </a> */ public SchemaBuilder allowMalformedNamesAndOptions( final boolean allowMalformedNamesAndOptions) { this.allowMalformedNamesAndOptions = allowMalformedNamesAndOptions; return this; } /** * Specifies whether or not the Telephone Number syntax should allow values * which do not conform to the E.123 international telephone number format. * <p> * By default this compatibility option is set to {@code true}. * * @param allowNonStandardTelephoneNumbers * {@code true} if the Telephone Number syntax should allow values * which do not conform to the E.123 international telephone number * format. * @return A reference to this {@code SchemaBuilder}. */ public SchemaBuilder allowNonStandardTelephoneNumbers( final boolean allowNonStandardTelephoneNumbers) { this.allowNonStandardTelephoneNumbers = allowNonStandardTelephoneNumbers; return this; } /** * Specifies whether or not zero-length values will be allowed by the * Directory String syntax. This is technically forbidden by the LDAP * specification, but it was allowed in earlier versions of the server, and * the discussion of the directory string syntax in RFC 2252 does not * explicitly state that they are not allowed. * <p> * By default this compatibility option is set to {@code false}. * * @param allowZeroLengthDirectoryStrings * {@code true} if zero-length values will be allowed by the * Directory String syntax, or {@code false} if not. * @return A reference to this {@code SchemaBuilder}. */ public SchemaBuilder allowZeroLengthDirectoryStrings( final boolean allowZeroLengthDirectoryStrings) { this.allowZeroLengthDirectoryStrings = allowZeroLengthDirectoryStrings; return this; } /** * Removes the named attribute type from this schema builder. * * @param name @@ -2600,9 +2683,20 @@ */ public boolean removeAttributeType(final String name) { if (schema.hasAttributeType(name)) final AttributeType element = numericOID2AttributeTypes.get(name); if (element != null) { removeAttributeType(schema.getAttributeType(name)); removeAttributeType(element); return true; } final List<AttributeType> elements = name2AttributeTypes .get(toLowerCase(name)); if (elements != null) { for (final AttributeType e : elements) { removeAttributeType(e); } return true; } return false; @@ -2619,9 +2713,20 @@ */ public boolean removeDITContentRule(final String name) { if (schema.hasDITContentRule(name)) final DITContentRule element = numericOID2ContentRules.get(name); if (element != null) { removeDITContentRule(schema.getDITContentRule(name)); removeDITContentRule(element); return true; } final List<DITContentRule> elements = name2ContentRules .get(toLowerCase(name)); if (elements != null) { for (final DITContentRule e : elements) { removeDITContentRule(e); } return true; } return false; @@ -2636,11 +2741,12 @@ * The ID of the DIT structure rule to be removed. * @return {@code true} if the DIT structure rule was found. */ public boolean removeDITStructureRule(final Integer ruleID) public boolean removeDITStructureRule(final int ruleID) { if (schema.hasDITStructureRule(ruleID)) final DITStructureRule element = id2StructureRules.get(ruleID); if (element != null) { removeDITStructureRule(schema.getDITStructureRule(ruleID)); removeDITStructureRule(element); return true; } return false; @@ -2657,9 +2763,20 @@ */ public boolean removeMatchingRule(final String name) { if (schema.hasMatchingRule(name)) final MatchingRule element = numericOID2MatchingRules.get(name); if (element != null) { removeMatchingRule(schema.getMatchingRule(name)); removeMatchingRule(element); return true; } final List<MatchingRule> elements = name2MatchingRules .get(toLowerCase(name)); if (elements != null) { for (final MatchingRule e : elements) { removeMatchingRule(e); } return true; } return false; @@ -2676,9 +2793,20 @@ */ public boolean removeMatchingRuleUse(final String name) { if (schema.hasMatchingRuleUse(name)) final MatchingRuleUse element = numericOID2MatchingRuleUses.get(name); if (element != null) { removeMatchingRuleUse(schema.getMatchingRuleUse(name)); removeMatchingRuleUse(element); return true; } final List<MatchingRuleUse> elements = name2MatchingRuleUses .get(toLowerCase(name)); if (elements != null) { for (final MatchingRuleUse e : elements) { removeMatchingRuleUse(e); } return true; } return false; @@ -2695,9 +2823,19 @@ */ public boolean removeNameForm(final String name) { if (schema.hasNameForm(name)) final NameForm element = numericOID2NameForms.get(name); if (element != null) { removeNameForm(schema.getNameForm(name)); removeNameForm(element); return true; } final List<NameForm> elements = name2NameForms.get(toLowerCase(name)); if (elements != null) { for (final NameForm e : elements) { removeNameForm(e); } return true; } return false; @@ -2714,9 +2852,20 @@ */ public boolean removeObjectClass(final String name) { if (schema.hasObjectClass(name)) final ObjectClass element = numericOID2ObjectClasses.get(name); if (element != null) { removeObjectClass(schema.getObjectClass(name)); removeObjectClass(element); return true; } final List<ObjectClass> elements = name2ObjectClasses .get(toLowerCase(name)); if (elements != null) { for (final ObjectClass e : elements) { removeObjectClass(e); } return true; } return false; @@ -2733,9 +2882,10 @@ */ public boolean removeSyntax(final String numericOID) { if (schema.hasSyntax(numericOID)) final Syntax element = numericOID2Syntaxes.get(numericOID); if (element != null) { removeSyntax(schema.getSyntax(numericOID)); removeSyntax(element); return true; } return false; @@ -2744,28 +2894,6 @@ /** * Sets the schema compatibility options for this schema builder. The schema * builder maintains its own set of compatibility options, so subsequent * changes to the provided set of options will not impact this schema builder. * * @param options * The set of schema compatibility options that this schema builder * should use. * @return A reference to this schema builder. * @throws NullPointerException * If {@code options} was {@code null}. */ public SchemaBuilder setSchemaCompatOptions(final SchemaCompatOptions options) throws NullPointerException { Validator.ensureNotNull(options); this.options.assign(options); return this; } /** * Returns a strict {@code Schema} containing all of the schema elements * contained in this schema builder as well as the same set of schema * compatibility options. @@ -2779,10 +2907,19 @@ */ public Schema toSchema() { validate(); final Schema builtSchema = schema; final Schema schema = new Schema(schemaName, allowMalformedNamesAndOptions, allowNonStandardTelephoneNumbers, allowZeroLengthDirectoryStrings, numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses, numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms, numericOID2ContentRules, id2StructureRules, name2MatchingRules, name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses, name2NameForms, name2ContentRules, name2StructureRules, objectClass2NameForms, nameForm2StructureRules, warnings); validate(schema); initBuilder(null); return builtSchema; return schema; } @@ -2881,8 +3018,8 @@ if (!overwrite) { final LocalizableMessage message = ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_ID .get(rule.getNameOrRuleID(), rule.getRuleID(), conflictingRule .getNameOrRuleID()); .get(rule.getNameOrRuleID(), rule.getRuleID(), conflictingRule.getNameOrRuleID()); throw new ConflictingSchemaElementException(message); } removeDITStructureRule(conflictingRule); @@ -2963,8 +3100,8 @@ if (!overwrite) { final LocalizableMessage message = ERR_SCHEMA_CONFLICTING_MATCHING_RULE_USE .get(use.getNameOrOID(), use.getMatchingRuleOID(), conflictingUse .getNameOrOID()); .get(use.getNameOrOID(), use.getMatchingRuleOID(), conflictingUse.getNameOrOID()); throw new ConflictingSchemaElementException(message); } removeMatchingRuleUse(conflictingUse); @@ -3004,8 +3141,8 @@ if (!overwrite) { final LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OID .get(form.getNameOrOID(), form.getOID(), conflictingForm .getNameOrOID()); .get(form.getNameOrOID(), form.getOID(), conflictingForm.getNameOrOID()); throw new ConflictingSchemaElementException(message); } removeNameForm(conflictingForm); @@ -3097,6 +3234,15 @@ private void initBuilder(String schemaName) { if (schemaName == null) { schemaName = String.format("Schema#%d", nextSchemaID.getAndIncrement()); } this.schemaName = schemaName; allowMalformedNamesAndOptions = true; allowNonStandardTelephoneNumbers = true; allowZeroLengthDirectoryStrings = false; numericOID2Syntaxes = new LinkedHashMap<String, Syntax>(); numericOID2MatchingRules = new LinkedHashMap<String, MatchingRule>(); numericOID2MatchingRuleUses = new LinkedHashMap<String, MatchingRuleUse>(); @@ -3116,22 +3262,7 @@ objectClass2NameForms = new HashMap<String, List<NameForm>>(); nameForm2StructureRules = new HashMap<String, List<DITStructureRule>>(); options = SchemaCompatOptions.defaultOptions(); warnings = new LinkedList<LocalizableMessage>(); if (schemaName == null) { schemaName = String.format("Schema#%d", nextSchemaID.getAndIncrement()); } schema = new Schema(schemaName, numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses, numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms, numericOID2ContentRules, id2StructureRules, name2MatchingRules, name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses, name2NameForms, name2ContentRules, name2StructureRules, objectClass2NameForms, nameForm2StructureRules, options, warnings); } @@ -3306,7 +3437,7 @@ private void validate() private void validate(final Schema schema) { // Verify all references in all elements for (final Syntax syntax : numericOID2Syntaxes.values().toArray( @@ -3319,8 +3450,8 @@ catch (final SchemaException e) { removeSyntax(syntax); warnings.add(ERR_SYNTAX_VALIDATION_FAIL.get( syntax.toString(), e.getMessageObject())); warnings.add(ERR_SYNTAX_VALIDATION_FAIL.get(syntax.toString(), e.getMessageObject())); } } @@ -3349,8 +3480,8 @@ catch (final SchemaException e) { removeAttributeType(attribute); warnings.add(ERR_ATTR_TYPE_VALIDATION_FAIL.get(attribute.toString(), e .getMessageObject())); warnings.add(ERR_ATTR_TYPE_VALIDATION_FAIL.get(attribute.toString(), e.getMessageObject())); } } @@ -3413,8 +3544,8 @@ catch (final SchemaException e) { removeNameForm(form); warnings.add(ERR_NAMEFORM_VALIDATION_FAIL.get(form.toString(), e .getMessageObject())); warnings.add(ERR_NAMEFORM_VALIDATION_FAIL.get(form.toString(), e.getMessageObject())); } } opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaCompatOptions.java
File was deleted opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaValidationPolicy.java
New file @@ -0,0 +1,505 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opendj3/legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opendj3/legal-notices/CDDLv1_0.txt. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2011 ForgeRock AS */ package org.forgerock.opendj.ldap.schema; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.ErrorResultException; /** * This class provides various schema validation policy options for controlling * how entries should be validated against the directory schema. */ public final class SchemaValidationPolicy { /** * A call-back which will be called during DIT structure rule schema * validation in order to retrieve the parent of the entry being validated. */ public interface EntryResolver { /** * Returns the named entry in order to enforce DIT structure rules. * * @param dn * The name of the entry to be returned. * @return The named entry. * @throws ErrorResultException * If the entry could not be retrieved. */ Entry getEntry(DN dn) throws ErrorResultException; } /** * The schema validation policy. */ public static enum Policy { /** * Schema validation will not be performed. */ IGNORE, /** * Schema validation will be performed, but failures will not cause the * overall validation to fail. Error messages will be returned. */ WARN, /** * Schema validation will be performed and failures will cause the overall * validation to fail. Error messages will be returned. */ REJECT; private Policy() { // Nothing to do. } /** * Returns {@code true} if this policy is {@code IGNORE}. * * @return {@code true} if this policy is {@code IGNORE}. */ public boolean isIgnore() { return this == IGNORE; } /** * Returns {@code true} if this policy is {@code REJECT}. * * @return {@code true} if this policy is {@code REJECT}. */ public boolean isReject() { return this == REJECT; } /** * Returns {@code true} if this policy is {@code WARN}. * * @return {@code true} if this policy is {@code WARN}. */ public boolean isWarn() { return this == WARN; } /** * Returns {@code true} if this policy is {@code WARN} or {@code REJECT}. * * @return {@code true} if this policy is {@code WARN} or {@code REJECT}. */ public boolean needsChecking() { return this != IGNORE; } } /** * Creates a copy of the provided schema validation policy. * * @param policy * The policy to be copied. * @return The copy of the provided schema validation policy. */ public static SchemaValidationPolicy copyOf( final SchemaValidationPolicy policy) { return defaultPolicy().assign(policy); } /** * Creates a new schema validation policy with default settings. More * specifically: * <ul> * <li>Entries not having a single structural object class will be rejected * <li>Entries having attributes which are not permitted by its object classes * or DIT content rule (if present) will be rejected * <li>Entries not conforming to name forms will be rejected * <li>DIT structure rules will not be ignored * </ul> * * @return The new schema validation policy. */ public static SchemaValidationPolicy defaultPolicy() { return new SchemaValidationPolicy(); } /** * Creates a new schema validation policy which will not perform any schema * validation. * * @return The new schema validation policy. */ public static SchemaValidationPolicy ignoreAll() { return new SchemaValidationPolicy() .checkAttributesAndObjectClasses(Policy.IGNORE) .checkAttributeValues(Policy.IGNORE) .checkDITContentRules(Policy.IGNORE) .checkNameForms(Policy.IGNORE) .requireSingleStructuralObjectClass(Policy.IGNORE); } private Policy checkNameForms = Policy.REJECT; private Policy checkDITStructureRules = Policy.IGNORE; private Policy checkDITContentRules = Policy.REJECT; private Policy requireSingleStructuralObjectClass = Policy.REJECT; private Policy checkAttributesAndObjectClasses = Policy.REJECT; private Policy checkAttributeValues = Policy.REJECT; private EntryResolver checkDITStructureRulesEntryResolver = null; // Prevent direct instantiation. private SchemaValidationPolicy() { // Nothing to do. } /** * Returns the policy for verifying that the user attributes in an entry * conform to its object classes. More specifically, an entry must contain all * required user attributes, and must not contain any user attributes which * are not declared as required or optional by its object classes. * <p> * By default entries which have missing or additional user attributes will be * rejected. * * @return The policy for verifying that the user attributes in an entry * conform to its object classes. */ public Policy checkAttributesAndObjectClasses() { return checkAttributesAndObjectClasses; } /** * Specifies the policy for verifying that the user attributes in an entry * conform to its object classes. More specifically, an entry must contain all * required user attributes, and must not contain any user attributes which * are not declared as required or optional by its object classes. * <p> * By default entries which have missing or additional user attributes will be * rejected. * * @param policy * The policy for verifying that the user attributes in an entry * conform to its object classes. * @return A reference to this {@code SchemaValidationPolicy}. */ public SchemaValidationPolicy checkAttributesAndObjectClasses( final Policy policy) { this.checkAttributesAndObjectClasses = policy; return this; } /** * Returns the policy for verifying that the user attributes in an entry * conform to their associated attribute type descriptions. This may include: * <ul> * <li>checking that there is at least one value * <li>checking that single-valued attributes contain only a single value * <li>checking that there are no duplicate values according to the * attribute's default equality matching rule * <li>checking that attributes which require BER encoding specify the * {@code ;binary} attribute option * <li>checking that the values are valid according to the attribute's syntax. * </ul> * Schema validation implementations specify exactly which of the above checks * will be performed. * <p> * By default entries which have invalid attribute values will be rejected. * * @return The policy for verifying that the user attributes in an entry * conform to their associated attribute type descriptions. */ public Policy checkAttributeValues() { return checkAttributeValues; } /** * Specifies the policy for verifying that the user attributes in an entry * conform to their associated attribute type descriptions. This may include: * <ul> * <li>checking that there is at least one value * <li>checking that single-valued attributes contain only a single value * <li>checking that there are no duplicate values according to the * attribute's default equality matching rule * <li>checking that attributes which require BER encoding specify the * {@code ;binary} attribute option * <li>checking that the values are valid according to the attribute's syntax. * </ul> * Schema validation implementations specify exactly which of the above checks * will be performed. * <p> * By default entries which have invalid attribute values will be rejected. * * @param policy * The policy for verifying that the user attributes in an entry * conform to their associated attribute type descriptions. * @return A reference to this {@code SchemaValidationPolicy}. */ public SchemaValidationPolicy checkAttributeValues(final Policy policy) { this.checkAttributeValues = policy; return this; } /** * Returns the policy for validating entries against content rules defined in * the schema. * <p> * By default content rules will be ignored during validation. * * @return The policy for validating entries against content rules defined in * the schema. */ public Policy checkDITContentRules() { return checkDITContentRules; } /** * Specifies the policy for validating entries against content rules defined * in the schema. * <p> * By default content rules will be ignored during validation. * * @param policy * The policy for validating entries against content rules defined in * the schema. * @return A reference to this {@code SchemaValidationPolicy}. */ public SchemaValidationPolicy checkDITContentRules(final Policy policy) { this.checkDITContentRules = policy; return this; } /** * Returns the policy for validating entries against structure rules defined * in the schema. * <p> * By default structure rules will be ignored during validation. * * @return The policy for validating entries against structure rules defined * in the schema. */ public Policy checkDITStructureRules() { return checkDITStructureRules; } /** * Specifies the policy for validating entries against structure rules defined * in the schema. * <p> * By default structure rules will be ignored during validation. * * @param policy * The policy for validating entries against structure rules defined * in the schema. * @param resolver * The parent entry resolver which should be used for retrieving the * parent entry during DIT structure rule validation. * @return A reference to this {@code SchemaValidationPolicy}. * @throws IllegalArgumentException * If {@code resolver} was {@code null} and * {@code checkDITStructureRules} is either {@code WARN} or * {@code REJECT}. */ public SchemaValidationPolicy checkDITStructureRules(final Policy policy, final EntryResolver resolver) throws IllegalArgumentException { if (checkDITStructureRules.needsChecking() && resolver == null) { throw new IllegalArgumentException( "Validation of structure rules enabled by resolver was null"); } this.checkDITStructureRules = policy; this.checkDITStructureRulesEntryResolver = resolver; return this; } /** * Returns parent entry resolver which should be used for retrieving the * parent entry during DIT structure rule validation. * <p> * By default no resolver is defined because structure rules will be ignored * during validation. * * @return The parent entry resolver which should be used for retrieving the * parent entry during DIT structure rule validation. */ public EntryResolver checkDITStructureRulesEntryResolver() { return checkDITStructureRulesEntryResolver; } /** * Returns the policy for validating entries against name forms defined in the * schema. * <p> * By default name forms will be ignored during validation. * * @return The policy for validating entries against name forms defined in the * schema. */ public Policy checkNameForms() { return checkNameForms; } /** * Specifies the policy for validating entries against name forms defined in * the schema. * <p> * By default name forms will be ignored during validation. * * @param policy * The policy for validating entries against name forms defined in * the schema. * @return A reference to this {@code SchemaValidationPolicy}. */ public SchemaValidationPolicy checkNameForms(final Policy policy) { this.checkNameForms = policy; return this; } /** * Returns the policy for verifying that entries have only a single structural * object class. * <p> * By default entries which do not have a structural object class or which * have more than one structural object class will be rejected. * * @return The policy for checking that entries have one and only one * structural object class. */ public Policy requireSingleStructuralObjectClass() { return requireSingleStructuralObjectClass; } /** * Specifies the policy for verifying that entries have only a single * structural object class. * <p> * By default entries which do not have a structural object class or which * have more than one structural object class will be rejected. * * @param policy * The policy for checking that entries have one and only one * structural object class. * @return A reference to this {@code SchemaValidationPolicy}. */ public SchemaValidationPolicy requireSingleStructuralObjectClass( final Policy policy) { this.requireSingleStructuralObjectClass = policy; return this; } // Assigns the provided options to this set of options. SchemaValidationPolicy assign(final SchemaValidationPolicy policy) { this.checkAttributeValues = policy.checkAttributeValues; this.checkNameForms = policy.checkNameForms; this.checkAttributesAndObjectClasses = policy.checkAttributesAndObjectClasses; this.checkDITContentRules = policy.checkDITContentRules; this.checkDITStructureRules = policy.checkDITStructureRules; this.checkDITStructureRulesEntryResolver = policy.checkDITStructureRulesEntryResolver; this.requireSingleStructuralObjectClass = policy.requireSingleStructuralObjectClass; return this; } } opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSyntaxImpl.java
@@ -113,7 +113,7 @@ final int length = valueStr.length(); if (!schema.getSchemaCompatOptions().allowNonStandardTelephoneNumbers()) if (!schema.allowNonStandardTelephoneNumbers()) { // If the value does not start with a plus sign, then that's not // acceptable. opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFReader.java
@@ -47,6 +47,8 @@ import org.forgerock.i18n.LocalizedIllegalArgumentException; import org.forgerock.opendj.ldap.*; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.opendj.ldap.schema.UnknownSchemaElementException; import com.forgerock.opendj.util.Base64; @@ -232,7 +234,7 @@ Schema schema = Schema.getDefaultSchema().asNonStrictSchema(); boolean validateSchema = false; SchemaValidationPolicy schemaValidationPolicy = SchemaValidationPolicy.ignoreAll(); private final LDIFReaderImpl impl; @@ -589,7 +591,7 @@ final void readLDIFRecordAttributeValue(final LDIFRecord record, final boolean readLDIFRecordAttributeValue(final LDIFRecord record, final String ldifLine, final Entry entry, final List<LocalizableMessage> schemaErrors) throws DecodeException { @@ -606,14 +608,19 @@ { final LocalizableMessage message = ERR_LDIF_UNKNOWN_ATTRIBUTE_TYPE.get( record.lineNumber, entry.getName().toString(), attrDescr); if (validateSchema) switch (schemaValidationPolicy.checkAttributesAndObjectClasses()) { case REJECT: schemaErrors.add(message); return; } else { throw DecodeException.error(message); return false; case WARN: schemaErrors.add(message); return true; default: // Ignore // This should not happen: we should be using a non-strict schema for // this policy. throw new IllegalStateException("Schema is not consistent with policy", e); } } catch (final LocalizedIllegalArgumentException e) @@ -632,19 +639,29 @@ // known to violate the schema. if (isAttributeExcluded(attributeDescription)) { return; return true; } final Syntax syntax = attributeDescription.getAttributeType().getSyntax(); // Ensure that the binary option is present if required. if (!attributeDescription.getAttributeType().getSyntax() .isBEREncodingRequired()) if (!syntax.isBEREncodingRequired()) { if (validateSchema && attributeDescription.containsOption("binary")) if (schemaValidationPolicy.checkAttributeValues().needsChecking() && attributeDescription.containsOption("binary")) { final LocalizableMessage message = ERR_LDIF_UNEXPECTED_BINARY_OPTION .get(record.lineNumber, entry.getName().toString(), attrDescr); schemaErrors.add(message); return; if (schemaValidationPolicy.checkAttributeValues().isReject()) { return false; } else { // Skip to next attribute value. return true; } } } else @@ -652,64 +669,57 @@ attributeDescription = attributeDescription.withOption("binary"); } final boolean checkAttributeValues = schemaValidationPolicy .checkAttributeValues().needsChecking(); if (checkAttributeValues) { LocalizableMessageBuilder builder = new LocalizableMessageBuilder(); if (!syntax.valueIsAcceptable(value, builder)) { schemaErrors.add(builder.toMessage()); if (schemaValidationPolicy.checkAttributeValues().isReject()) { return false; } } } Attribute attribute = entry.getAttribute(attributeDescription); if (attribute == null) { if (validateSchema) { final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder(); if (!attributeDescription.getAttributeType().getSyntax() .valueIsAcceptable(value, invalidReason)) { final LocalizableMessage message = WARN_LDIF_MALFORMED_ATTRIBUTE_VALUE .get(record.lineNumber, entry.getName().toString(), value.toString(), attrDescr, invalidReason); schemaErrors.add(message); return; } } attribute = new LinkedAttribute(attributeDescription, value); entry.addAttribute(attribute); } else else if (checkAttributeValues) { if (validateSchema) { final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder(); if (!attributeDescription.getAttributeType().getSyntax() .valueIsAcceptable(value, invalidReason)) { final LocalizableMessage message = WARN_LDIF_MALFORMED_ATTRIBUTE_VALUE .get(record.lineNumber, entry.getName().toString(), value.toString(), attrDescr, invalidReason); schemaErrors.add(message); return; } if (!attribute.add(value) && validateSchema) if (!attribute.add(value)) { final LocalizableMessage message = WARN_LDIF_DUPLICATE_ATTRIBUTE_VALUE .get(record.lineNumber, entry.getName().toString(), attrDescr, value.toString()); schemaErrors.add(message); return; if (schemaValidationPolicy.checkAttributeValues().isReject()) { return false; } if (validateSchema && attributeDescription.getAttributeType().isSingleValue()) } else if (attributeDescription.getAttributeType().isSingleValue()) { final LocalizableMessage message = ERR_LDIF_MULTI_VALUED_SINGLE_VALUED_ATTRIBUTE .get(record.lineNumber, entry.getName().toString(), attrDescr); schemaErrors.add(message); return; if (schemaValidationPolicy.checkAttributeValues().isReject()) { return false; } } } else { attribute.add(value); } } return true; } @@ -893,6 +903,15 @@ final void handleSchemaValidationWarning(final LDIFRecord record, final List<LocalizableMessage> messages) throws DecodeException { rejectedRecordListener.handleSchemaValidationWarning(record.lineNumber, record.ldifLines, messages); } final void handleSkippedRecord(final LDIFRecord record, final LocalizableMessage message) throws DecodeException { opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordReader.java
@@ -38,12 +38,15 @@ import java.util.*; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageBuilder; import org.forgerock.i18n.LocalizedIllegalArgumentException; import org.forgerock.opendj.ldap.*; import org.forgerock.opendj.ldap.requests.ModifyDNRequest; import org.forgerock.opendj.ldap.requests.ModifyRequest; import org.forgerock.opendj.ldap.requests.Requests; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.opendj.ldap.schema.UnknownSchemaElementException; import com.forgerock.opendj.util.Validator; @@ -363,32 +366,34 @@ public LDIFChangeRecordReader setSchema(final Schema schema) { Validator.ensureNotNull(schema); this.schema = validateSchema ? schema.asStrictSchema() : schema .asNonStrictSchema(); this.schema = schemaValidationPolicy.checkAttributesAndObjectClasses() .needsChecking() ? schema.asStrictSchema() : schema.asNonStrictSchema(); return this; } /** * Specifies whether or not schema validation should be performed for change * records that are read from LDIF. * <p> * When enabled, the LDIF reader will implicitly use a strict schema so that * unrecognized attribute types will be detected. * Specifies the schema validation which should be used when reading LDIF * change records. If attribute value validation is enabled then all checks * will be performed. * <p> * Schema validation is disabled by default. * <p> * <b>NOTE:</b> this method copies the provided policy so changes made to it * after this method has been called will have no effect. * * @param validateSchema * {@code true} if schema validation should be performed, or * {@code false} otherwise. * @param policy * The schema validation which should be used when reading LDIF * change records. * @return A reference to this {@code LDIFChangeRecordReader}. */ public LDIFChangeRecordReader setValidateSchema(final boolean validateSchema) public LDIFChangeRecordReader setSchemaValidationPolicy( final SchemaValidationPolicy policy) { this.validateSchema = validateSchema; this.schema = validateSchema ? schema.asStrictSchema() : schema .asNonStrictSchema(); this.schemaValidationPolicy = SchemaValidationPolicy.copyOf(policy); this.schema = schemaValidationPolicy.checkAttributesAndObjectClasses() .needsChecking() ? schema.asStrictSchema() : schema.asNonStrictSchema(); return this; } @@ -497,24 +502,42 @@ { // Use an Entry for the AttributeSequence. final Entry entry = new LinkedHashMapEntry(entryDN); boolean schemaValidationFailure = false; final List<LocalizableMessage> schemaErrors = new LinkedList<LocalizableMessage>(); if (lastLDIFLine != null) { // This line was read when looking for the change type. readLDIFRecordAttributeValue(record, lastLDIFLine, entry, schemaErrors); if (!readLDIFRecordAttributeValue(record, lastLDIFLine, entry, schemaErrors)) { schemaValidationFailure = true; } } while (record.iterator.hasNext()) { final String ldifLine = record.iterator.next(); readLDIFRecordAttributeValue(record, ldifLine, entry, schemaErrors); if (!readLDIFRecordAttributeValue(record, ldifLine, entry, schemaErrors)) { schemaValidationFailure = true; } } if (!schema.validateEntry(entry, schemaValidationPolicy, schemaErrors)) { schemaValidationFailure = true; } if (schemaValidationFailure) { handleSchemaValidationFailure(record, schemaErrors); return null; } if (!schemaErrors.isEmpty()) { handleSchemaValidationFailure(record, schemaErrors); return null; handleSchemaValidationWarning(record, schemaErrors); } return Requests.newAddRequest(entry); @@ -541,9 +564,9 @@ final LDIFRecord record) throws DecodeException { final ModifyRequest modifyRequest = Requests.newModifyRequest(entryDN); final KeyValuePair pair = new KeyValuePair(); final List<ByteString> attributeValues = new ArrayList<ByteString>(); boolean schemaValidationFailure = false; final List<LocalizableMessage> schemaErrors = new LinkedList<LocalizableMessage>(); while (record.iterator.hasNext()) @@ -591,14 +614,20 @@ { final LocalizableMessage message = ERR_LDIF_UNKNOWN_ATTRIBUTE_TYPE.get( record.lineNumber, entryDN.toString(), pair.value); if (validateSchema) switch (schemaValidationPolicy.checkAttributesAndObjectClasses()) { case REJECT: schemaValidationFailure = true; schemaErrors.add(message); continue; } else { throw DecodeException.error(message); case WARN: schemaErrors.add(message); continue; default: //Ignore // This should not happen: we should be using a non-strict schema for // this policy. throw new IllegalStateException( "Schema is not consistent with policy", e); } } catch (final LocalizedIllegalArgumentException e) @@ -616,14 +645,20 @@ continue; } final Syntax syntax = attributeDescription.getAttributeType().getSyntax(); // Ensure that the binary option is present if required. if (!attributeDescription.getAttributeType().getSyntax() .isBEREncodingRequired()) if (!syntax.isBEREncodingRequired()) { if (validateSchema && attributeDescription.containsOption("binary")) if (schemaValidationPolicy.checkAttributeValues().needsChecking() && attributeDescription.containsOption("binary")) { final LocalizableMessage message = ERR_LDIF_UNEXPECTED_BINARY_OPTION .get(record.lineNumber, entryDN.toString(), pair.value); if (schemaValidationPolicy.checkAttributeValues().isReject()) { schemaValidationFailure = true; } schemaErrors.add(message); continue; } @@ -657,7 +692,7 @@ catch (final LocalizedIllegalArgumentException e) { // No need to catch schema exception here because it implies that the // attribute name is wrong. // attribute name is wrong and the record is malformed. final LocalizableMessage message = ERR_LDIF_MALFORMED_ATTRIBUTE_NAME .get(record.lineNumber, entryDN.toString(), attrDescr); throw DecodeException.error(message); @@ -672,6 +707,7 @@ if (!attributeDescription2.equals(attributeDescription)) { // Malformed record. final LocalizableMessage message = ERR_LDIF_ATTRIBUTE_NAME_MISMATCH .get(record.lineNumber, entryDN.toString(), attributeDescription2.toString(), @@ -679,9 +715,26 @@ throw DecodeException.error(message); } // Now parse the attribute value. attributeValues.add(parseSingleValue(record, ldifLine, entryDN, colonPos, attrDescr)); // Parse the attribute value and check it if needed. final ByteString value = parseSingleValue(record, ldifLine, entryDN, colonPos, attrDescr); if (schemaValidationPolicy.checkAttributeValues().needsChecking()) { LocalizableMessageBuilder builder = new LocalizableMessageBuilder(); if (!syntax.valueIsAcceptable(value, builder)) { // Just log a message, but don't skip the value since this could // change the semantics of the modification (e.g. if all values in a // delete are skipped then this implies that the whole attribute // should be removed). if (schemaValidationPolicy.checkAttributeValues().isReject()) { schemaValidationFailure = true; } schemaErrors.add(builder.toMessage()); } } attributeValues.add(value); } final Modification change = new Modification(modType, @@ -689,12 +742,17 @@ modifyRequest.addModification(change); } if (!schemaErrors.isEmpty()) if (schemaValidationFailure) { handleSchemaValidationFailure(record, schemaErrors); return null; } if (!schemaErrors.isEmpty()) { handleSchemaValidationWarning(record, schemaErrors); } return modifyRequest; } opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryReader.java
@@ -43,6 +43,7 @@ import org.forgerock.i18n.LocalizedIllegalArgumentException; import org.forgerock.opendj.ldap.*; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy; import com.forgerock.opendj.util.Validator; @@ -393,32 +394,34 @@ public LDIFEntryReader setSchema(final Schema schema) { Validator.ensureNotNull(schema); this.schema = validateSchema ? schema.asStrictSchema() : schema .asNonStrictSchema(); this.schema = schemaValidationPolicy.checkAttributesAndObjectClasses() .needsChecking() ? schema.asStrictSchema() : schema.asNonStrictSchema(); return this; } /** * Specifies whether or not schema validation should be performed for entries * that are read from LDIF. * <p> * When enabled, this LDIF reader will implicitly use a strict schema so that * unrecognized attribute types will be detected. * Specifies the schema validation which should be used when reading LDIF * entry records. If attribute value validation is enabled then all checks * will be performed. * <p> * Schema validation is disabled by default. * <p> * <b>NOTE:</b> this method copies the provided policy so changes made to it * after this method has been called will have no effect. * * @param validateSchema * {@code true} if schema validation should be performed, or * {@code false} otherwise. * @param policy * The schema validation which should be used when reading LDIF entry * records. * @return A reference to this {@code LDIFEntryReader}. */ public LDIFEntryReader setValidateSchema(final boolean validateSchema) public LDIFEntryReader setSchemaValidationPolicy( final SchemaValidationPolicy policy) { this.validateSchema = validateSchema; this.schema = validateSchema ? schema.asStrictSchema() : schema .asNonStrictSchema(); this.schemaValidationPolicy = SchemaValidationPolicy.copyOf(policy); this.schema = schemaValidationPolicy.checkAttributesAndObjectClasses() .needsChecking() ? schema.asStrictSchema() : schema.asNonStrictSchema(); return this; } @@ -458,11 +461,16 @@ // Use an Entry for the AttributeSequence. final Entry entry = new LinkedHashMapEntry(entryDN); boolean schemaValidationFailure = false; final List<LocalizableMessage> schemaErrors = new LinkedList<LocalizableMessage>(); while (record.iterator.hasNext()) { final String ldifLine = record.iterator.next(); readLDIFRecordAttributeValue(record, ldifLine, entry, schemaErrors); if (!readLDIFRecordAttributeValue(record, ldifLine, entry, schemaErrors)) { schemaValidationFailure = true; } } // Skip if the entry is excluded by any filters. @@ -474,12 +482,22 @@ continue; } if (!schemaErrors.isEmpty()) if (!schema.validateEntry(entry, schemaValidationPolicy, schemaErrors)) { schemaValidationFailure = true; } if (schemaValidationFailure) { handleSchemaValidationFailure(record, schemaErrors); continue; } if (!schemaErrors.isEmpty()) { handleSchemaValidationWarning(record, schemaErrors); } nextEntry = entry; } catch (final DecodeException e) opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java
@@ -78,6 +78,15 @@ { // Ignore skipped records. } public void handleSchemaValidationWarning(long lineNumber, List<String> ldifRecord, List<LocalizableMessage> reasons) throws DecodeException { // Ignore schema validation warnings. } }; @@ -122,6 +131,25 @@ /** * Invoked when a record was not rejected but contained one or more schema validation warnings. * * @param lineNumber * The line number within the source location in which the * record is located, if known, otherwise {@code -1}. * @param ldifRecord * An LDIF representation of the record which contained schema * validation warnings. * @param reasons * The schema validation warnings. * @throws DecodeException * If processing should terminate. */ void handleSchemaValidationWarning(long lineNumber, List<String> ldifRecord, List<LocalizableMessage> reasons) throws DecodeException; /** * Invoked when a record was skipped because it did not match filter criteria * defined by the reader. * opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties
@@ -1328,4 +1328,47 @@ with distinguished name "%s" because it contained a malformed deleteoldrdn "%s" ERR_LDIF_MALFORMED_NEW_SUPERIOR=Unable to parse LDIF modify DN record starting at line %d \ with distinguished name "%s" because it contained a malformed newsuperior "%s" ERR_ENTRY_SCHEMA_MULTIPLE_STRUCTURAL_CLASSES=Entry "%s" violates the schema \ because it contains multiple conflicting structural object classes "%s" and \ "%s". Only a single structural object class is allowed in an entry ERR_ENTRY_SCHEMA_UNKNOWN_OBJECT_CLASS=Entry "%s" violates the schema \ because it contains an unrecognized object class "%s" ERR_ENTRY_SCHEMA_NO_STRUCTURAL_CLASS=Entry "%s" violates the schema because \ it does not include a structural object class. All entries must contain a \ structural object class ERR_ENTRY_SCHEMA_DCR_MISSING_MUST_ATTRIBUTES=Entry "%s" violates the schema \ because it does not contain attribute "%s" which is required by DIT content rule "%s" ERR_ENTRY_SCHEMA_DCR_PROHIBITED_ATTRIBUTES=Entry "%s" violates the schema \ because it contains attribute "%s" which is prohibited by DIT content rule "%s" ERR_ENTRY_SCHEMA_DCR_PROHIBITED_AUXILIARY_OC=Entry "%s" violates the schema because \ it contains auxiliary object class "%s" which is not allowed by DIT content rule "%s" ERR_ENTRY_SCHEMA_OC_MISSING_MUST_ATTRIBUTES=Entry "%s" violates the schema \ because it does not contain attribute "%s" which is required by object class "%s" ERR_ENTRY_SCHEMA_OC_DISALLOWED_ATTRIBUTES=Entry "%s" violates the schema \ because it contains attribute "%s" which is not allowed by any of the object \ classes in the entry ERR_ENTRY_SCHEMA_DCR_DISALLOWED_ATTRIBUTES=Entry "%s" violates the schema \ because it contains attribute "%s" which is not allowed by any of the object \ classes in the entry nor its DIT content rule "%s" ERR_ENTRY_SCHEMA_AT_EMPTY_ATTRIBUTE=Entry "%s" violates the schema \ because it contains an empty attribute "%s" ERR_ENTRY_SCHEMA_AT_SINGLE_VALUED_ATTRIBUTE=Entry "%s" violates the schema \ because it contains multiple values for the single-valued attribute "%s" ERR_ENTRY_SCHEMA_NF_MISSING_MUST_ATTRIBUTES=Entry "%s" violates the schema \ because its RDN does not contain the attribute "%s" which is required by \ name form "%s" ERR_ENTRY_SCHEMA_NF_DISALLOWED_ATTRIBUTES=Entry "%s" violates the schema \ because its RDN contains attribute "%s" which is not allowed by any the name \ form "%s" ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC=Entry "%s" could not be validated against \ any DIT structure rules because its parent entry does not appear to \ contain a valid structural object class ERR_ENTRY_SCHEMA_DSR_PARENT_NOT_FOUND=Entry "%s" could not be validated against \ any DIT structure rules because its parent entry could not be retrieved \ for the following reason: %s ERR_ENTRY_SCHEMA_DSR_ILLEGAL_OC=Entry "%s" violates the schema because DIT \ structure rule "%s" does not allow entries of type "%s" to be placed \ immediately below entries of type "%s" ERR_ENTRY_SCHEMA_DSR_MISSING_DSR=Entry "%s" violates the schema because \ there is no DIT structure rule that applies to the entry, but there is a DIT \ structure rule "%s" which applies to the parent entry opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatTest.java
File was renamed from opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatOptionsTest.java @@ -36,9 +36,9 @@ /** * Test SchemaCompatOptions. * Tests schema compatibility options. */ public class SchemaCompatOptionsTest extends SchemaTestCase public class SchemaCompatTest extends SchemaTestCase { /** * Returns test data for valid attribute descriptions. @@ -85,8 +85,7 @@ boolean allowIllegalCharacters) { SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema()) .setSchemaCompatOptions(SchemaCompatOptions.defaultOptions() .allowMalformedNamesAndOptions(allowIllegalCharacters)); .allowMalformedNamesAndOptions(allowIllegalCharacters); Schema schema = builder.toSchema().asNonStrictSchema(); AttributeDescription.valueOf(atd, schema); } @@ -134,8 +133,7 @@ boolean allowIllegalCharacters) { SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema()) .setSchemaCompatOptions(SchemaCompatOptions.defaultOptions() .allowMalformedNamesAndOptions(allowIllegalCharacters)); .allowMalformedNamesAndOptions(allowIllegalCharacters); Schema schema = builder.toSchema().asNonStrictSchema(); AttributeDescription.valueOf(atd, schema); } @@ -237,8 +235,7 @@ Syntax syntax, boolean allowIllegalCharacters) { SchemaBuilder builder = new SchemaBuilder() .setSchemaCompatOptions(SchemaCompatOptions.defaultOptions() .allowMalformedNamesAndOptions(allowIllegalCharacters)); .allowMalformedNamesAndOptions(allowIllegalCharacters); if (syntax == ATD_SYNTAX) { @@ -340,8 +337,7 @@ Syntax syntax, boolean allowIllegalCharacters) { SchemaBuilder builder = new SchemaBuilder() .setSchemaCompatOptions(SchemaCompatOptions.defaultOptions() .allowMalformedNamesAndOptions(allowIllegalCharacters)); .allowMalformedNamesAndOptions(allowIllegalCharacters); if (syntax == ATD_SYNTAX) { opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java
@@ -42,6 +42,8 @@ import org.forgerock.opendj.ldap.requests.DeleteRequest; import org.forgerock.opendj.ldap.requests.ModifyDNRequest; import org.forgerock.opendj.ldap.requests.ModifyRequest; import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy; import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.Policy; import org.testng.annotations.Test; @@ -457,7 +459,10 @@ "objectClass: domainComponent", "dc: example", "xxx: unknown attribute" ).setRejectedRecordListener(listener).setValidateSchema(true); ).setRejectedRecordListener(listener) .setSchemaValidationPolicy( SchemaValidationPolicy.ignoreAll() .checkAttributesAndObjectClasses(Policy.REJECT)); // @formatter:on assertThat(reader.hasNext()).isFalse(); @@ -468,4 +473,53 @@ "objectClass: top", "objectClass: domainComponent", "dc: example", "xxx: unknown attribute")), anyListOf(LocalizableMessage.class)); } /** * Tests reading a record which does not conform to the schema invokes the * warning record listener. * * @throws Exception * if an unexpected error occurred. */ @Test public void testRejectedRecordListenerWarnsBadSchemaRecord() throws Exception { RejectedRecordListener listener = mock(RejectedRecordListener.class); // @formatter:off LDIFChangeRecordReader reader = new LDIFChangeRecordReader( "dn: dc=example,dc=com", "changetype: add", "objectClass: top", "objectClass: domainComponent", "dc: example", "xxx: unknown attribute" ).setRejectedRecordListener(listener) .setSchemaValidationPolicy( SchemaValidationPolicy.ignoreAll() .checkAttributesAndObjectClasses(Policy.WARN)); // @formatter:on assertThat(reader.hasNext()).isTrue(); ChangeRecord record = reader.readChangeRecord(); assertThat(record).isInstanceOf(AddRequest.class); AddRequest addRequest = (AddRequest) record; assertThat((Object) addRequest.getName()).isEqualTo( DN.valueOf("dc=example,dc=com")); assertThat( addRequest.containsAttribute("objectClass", "top", "domainComponent")) .isTrue(); assertThat(addRequest.containsAttribute("dc", "example")).isTrue(); assertThat(addRequest.getAttributeCount()).isEqualTo(2); verify(listener).handleSchemaValidationWarning( eq(1L), eq(Arrays.asList("dn: dc=example,dc=com", "changetype: add", "objectClass: top", "objectClass: domainComponent", "dc: example", "xxx: unknown attribute")), anyListOf(LocalizableMessage.class)); } } opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFEntryReaderTestCase.java
@@ -63,8 +63,6 @@ final LDIFEntryReader reader = new LDIFEntryReader(in); try { reader.setValidateSchema(false); Assert.assertFalse(reader.hasNext()); Assert.assertFalse(reader.hasNext()); try @@ -126,8 +124,6 @@ final LDIFEntryReader reader = new LDIFEntryReader(in); try { reader.setValidateSchema(false); Assert.assertTrue(reader.hasNext()); final Entry entry = reader.readEntry(); assertNotNull(entry);