opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java
@@ -14,7 +14,6 @@ * Copyright 2009 Sun Microsystems, Inc. * Portions copyright 2015-2016 ForgeRock AS. */ package org.forgerock.opendj.ldap.schema; import static java.util.Arrays.*; @@ -51,7 +50,7 @@ private final Set<Integer> superiorRuleIDs = new LinkedHashSet<>(); Builder(final DITStructureRule structureRule, final SchemaBuilder builder) { super(builder); super(builder, structureRule); this.ruleID = structureRule.ruleID; this.names.addAll(structureRule.names); this.isObsolete = structureRule.isObsolete; @@ -252,7 +251,6 @@ this.superiorRuleIDs.addAll(superiorRuleIDs); return this; } } /** The rule ID for this DIT structure rule. */ @@ -460,9 +458,8 @@ boolean validate(final Schema schema, final List<DITStructureRule> invalidSchemaElements, final List<LocalizableMessage> warnings) { // Avoid validating this schema element more than once. This may occur // if // multiple rules specify the same superior. // Avoid validating this schema element more than once. // This may occur if multiple rules specify the same superior. if (!needsValidating) { return isValid; } opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DITStructureRuleTestCase.java
@@ -11,10 +11,11 @@ * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2015 ForgeRock AS. * Copyright 2015-2016 ForgeRock AS. */ package org.forgerock.opendj.ldap.schema; import static java.util.Arrays.*; import static java.util.Collections.*; import static org.fest.assertions.Assertions.*; @@ -75,6 +76,7 @@ .names("DIT structure rule test") .nameForm(NAME_FORM_TEST_OID) .description("My DIT structure rule") .extraProperties("X-SCHEMA-FILE", "99-user.ldif") .addToSchema() .toSchema(); @@ -93,8 +95,9 @@ assertThat(srCopy.getRuleID()).isEqualTo(43); assertThat(srCopy.getNames()).containsOnly("DIT structure rule test", "DIT structure rule test - copy"); assertThat(srCopy.getNameForm().getOID()).isEqualTo(NAME_FORM_TEST_OID); assertThat(srCopy.getDescription()).isEmpty(); assertThat(srCopy.getDescription()).isEqualTo("My DIT structure rule"); assertThat(srCopy.isObsolete()).isFalse(); assertThat(srCopy.getExtraProperties()).includes(entry("X-SCHEMA-FILE", asList("99-user.ldif"))); } @Test(expectedExceptions = ConflictingSchemaElementException.class) opendj-server-legacy/src/main/java/org/opends/server/backends/SchemaBackend.java
@@ -63,6 +63,7 @@ import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.CoreSchema; import org.forgerock.opendj.ldap.schema.DITContentRule; import org.forgerock.opendj.ldap.schema.DITStructureRule; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.NameForm; @@ -82,7 +83,6 @@ import org.opends.server.core.SearchOperation; import org.opends.server.core.ServerContext; import org.opends.server.schema.AttributeTypeSyntax; import org.opends.server.schema.DITStructureRuleSyntax; import org.opends.server.schema.GeneralizedTimeSyntax; import org.opends.server.schema.ServerSchemaElement; import org.opends.server.schema.SomeSchemaElement; @@ -91,7 +91,6 @@ import org.opends.server.types.Attributes; import org.opends.server.types.BackupConfig; import org.opends.server.types.BackupDirectory; import org.opends.server.types.DITStructureRule; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.ExistingFileBehavior; @@ -581,7 +580,7 @@ operationalAttrs, nameFormsType, includeSchemaFile, false, true); buildSchemaAttribute(schema.getDITContentRules(), userAttrs, operationalAttrs, ditContentRulesType, includeSchemaFile, false, true); buildSchemaAttribute(schema.getDITStructureRulesByID().values(), userAttrs, buildSchemaAttribute(schema.getDITStructureRules(), userAttrs, operationalAttrs, ditStructureRulesType, includeSchemaFile, false, true); buildSchemaAttribute(schema.getMatchingRuleUses(), userAttrs, operationalAttrs, matchingRuleUsesType, includeSchemaFile, false, true); @@ -782,21 +781,7 @@ { for (ByteString v : a) { DITStructureRule dsr; try { dsr = DITStructureRuleSyntax.decodeDITStructureRule(v, newSchema, false); } catch (DirectoryException de) { logger.traceException(de); LocalizableMessage message = ERR_SCHEMA_MODIFY_CANNOT_DECODE_DSR.get( v, de.getMessageObject()); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, de); } DITStructureRule dsr = newSchema.parseDITStructureRule(v.toString()); addDITStructureRule(dsr, newSchema, modifiedSchemaFiles); } } @@ -878,23 +863,8 @@ { for (ByteString v : a) { DITStructureRule dsr; try { dsr = DITStructureRuleSyntax.decodeDITStructureRule(v, newSchema, false); } catch (DirectoryException de) { logger.traceException(de); LocalizableMessage message = ERR_SCHEMA_MODIFY_CANNOT_DECODE_DSR.get( v, de.getMessageObject()); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, de); } removeDITStructureRule(dsr, newSchema, mods, pos, modifiedSchemaFiles); DITStructureRule dsr = newSchema.parseDITStructureRule(v.toString()); removeDITStructureRule(dsr, newSchema, mods, pos, modifiedSchemaFiles); } } else if (at.equals(matchingRuleUsesType)) @@ -1116,17 +1086,6 @@ } } private void addNewSchemaElement(Set<String> modifiedSchemaFiles, SchemaElement elem) { String schemaFile = getSchemaFile(elem); if (schemaFile == null || schemaFile.length() == 0) { schemaFile = FILE_USER_SCHEMA_ELEMENTS; setSchemaFile(elem, schemaFile); } modifiedSchemaFiles.add(schemaFile); } /** * Update list of modified files and return the schema file to use for the * added element (may be null). @@ -1139,32 +1098,6 @@ return schemaFile == null ? finalFile : null; } private <T extends SchemaElement> void replaceExistingSchemaElement( Set<String> modifiedSchemaFiles, T newElem, T existingElem) { String newSchemaFile = getSchemaFile(newElem); String oldSchemaFile = getSchemaFile(existingElem); if (newSchemaFile == null || newSchemaFile.length() == 0) { if (oldSchemaFile == null || oldSchemaFile.length() == 0) { oldSchemaFile = FILE_USER_SCHEMA_ELEMENTS; } setSchemaFile(newElem, oldSchemaFile); modifiedSchemaFiles.add(oldSchemaFile); } else if (oldSchemaFile == null || oldSchemaFile.equals(newSchemaFile)) { modifiedSchemaFiles.add(newSchemaFile); } else { modifiedSchemaFiles.add(newSchemaFile); modifiedSchemaFiles.add(oldSchemaFile); } } /** * Update list of modified files and return the schema file to use for the new * element (may be null). @@ -1459,7 +1392,7 @@ String oid; try { oid = schema.parseOID(v.toString(), ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_OBJECTCLASS_OID); oid = Schema.parseOID(v.toString(), ERR_PARSING_OBJECTCLASS_OID); } catch (DirectoryException de) { @@ -1679,15 +1612,15 @@ } // Make sure that the name form isn't referenced by any DIT structure rule. DITStructureRule dsr = schema.getDITStructureRule(removeNF); if (dsr != null) Collection<DITStructureRule> ditRules = schema.getDITStructureRules(removeNF); if (!ditRules.isEmpty()) { LocalizableMessage message = ERR_SCHEMA_MODIFY_REMOVE_NF_IN_DSR.get( removeNF.getNameOrOID(), dsr.getNameOrRuleID()); removeNF.getNameOrOID(), ditRules.iterator().next().getNameOrRuleID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } // If we've gotten here, then it's OK to remove the name form from the schema. // Now remove the name form from the schema. schema.deregisterNameForm(removeNF); String schemaFile = getSchemaFile(removeNF); if (schemaFile != null) @@ -1886,13 +1819,14 @@ // check the rule ID and all of the names, which means that it's possible // there could be more than one match (although if there is, then we'll // refuse the operation). DITStructureRule existingDSR = schema.getDITStructureRule(ditStructureRule.getRuleID()); //Boolean to check if the new rule is in use or not. boolean inUse = false; for (DITStructureRule dsr : schema.getDITStructureRulesByID().values()) final org.forgerock.opendj.ldap.schema.Schema schemaNG = schema.getSchemaNG(); final Integer ruleID = ditStructureRule.getRuleID(); DITStructureRule existingDSR = schemaNG.hasDITStructureRule(ruleID) ? schemaNG.getDITStructureRule(ruleID) : null; boolean newRuleIsInUse = false; for (DITStructureRule dsr : schema.getDITStructureRules()) { for (String name : ditStructureRule.getNames().keySet()) for (String name : ditStructureRule.getNames()) { if (dsr.hasName(name)) { @@ -1906,12 +1840,12 @@ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } inUse = true; newRuleIsInUse = true; } } } if(existingDSR != null && !inUse) if (existingDSR != null && !newRuleIsInUse) { //We have an existing DSR with the same rule id but we couldn't find //any existing rules sharing this name. It means that it is a @@ -1920,35 +1854,12 @@ LocalizableMessage message = ERR_SCHEMA_MODIFY_RULEID_CONFLICTS_FOR_ADD_DSR. get(ditStructureRule.getNameOrRuleID(), existingDSR.getNameOrRuleID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } // Get the name form for the new DIT structure rule and see if there's // already an existing rule that is associated with that name form. If // there is, then it will only be acceptable if it's the DIT structure rule // that we are replacing (in which case we really do want to use the "!=" // operator). NameForm nameForm = ditStructureRule.getNameForm(); DITStructureRule existingRuleForNameForm = schema.getDITStructureRule(nameForm); if (existingRuleForNameForm != null && existingRuleForNameForm != existingDSR) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NAME_FORM_CONFLICT_FOR_ADD_DSR. get(ditStructureRule.getNameOrRuleID(), nameForm.getNameOrOID(), existingRuleForNameForm.getNameOrRuleID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } // Make sure that the new DIT structure rule doesn't reference an undefined // name form or superior DIT structure rule. if (! schema.hasNameForm(nameForm.getOID())) { LocalizableMessage message = ERR_SCHEMA_MODIFY_DSR_UNDEFINED_NAME_FORM.get( ditStructureRule.getNameOrRuleID(), nameForm.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } NameForm nameForm = ditStructureRule.getNameForm(); if (nameForm.isObsolete()) { LocalizableMessage message = ERR_SCHEMA_MODIFY_DSR_OBSOLETE_NAME_FORM.get( @@ -1972,16 +1883,16 @@ // Otherwise, we're replacing an existing one. if (existingDSR == null) { schema.registerDITStructureRule(ditStructureRule, false); addNewSchemaElement(modifiedSchemaFiles, ditStructureRule); String schemaFile = addNewSchemaElement(modifiedSchemaFiles, new ServerSchemaElement(ditStructureRule)); schema.registerDITStructureRule(ditStructureRule, schemaFile, false); } else { schema.deregisterDITStructureRule(existingDSR); schema.registerDITStructureRule(ditStructureRule, false); String schemaFile = replaceExistingSchemaElement( modifiedSchemaFiles, new ServerSchemaElement(ditStructureRule), new ServerSchemaElement(existingDSR)); schema.registerDITStructureRule(ditStructureRule, schemaFile, false); schema.rebuildDependentElements(existingDSR); replaceExistingSchemaElement(modifiedSchemaFiles, ditStructureRule, existingDSR); } } @@ -2045,21 +1956,7 @@ for (ByteString v : a) { DITStructureRule dsr; try { dsr = DITStructureRuleSyntax.decodeDITStructureRule(v, schema, true); } catch (DirectoryException de) { logger.traceException(de); LocalizableMessage message = ERR_SCHEMA_MODIFY_CANNOT_DECODE_DSR.get( v, de.getMessageObject()); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, de); } DITStructureRule dsr = schema.parseDITStructureRule(v.toString()); if (ditStructureRule.getRuleID() == dsr.getRuleID()) { // We found a match where the DIT structure rule is added back later, @@ -2071,7 +1968,7 @@ // Make sure that the DIT structure rule isn't the superior for any other // DIT structure rule. for (DITStructureRule dsr : schema.getDITStructureRulesByID().values()) for (DITStructureRule dsr : schema.getDITStructureRules()) { if (dsr.getSuperiorRules().contains(removeDSR)) { @@ -2240,9 +2137,7 @@ private void addLdapSyntaxDescription(final String definition, Schema schema, Set<String> modifiedSchemaFiles) throws DirectoryException { // Check if there is an existing syntax with this oid. String oid = Schema.parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_LDAP_SYNTAX_OID); String oid = Schema.parseOID(definition, ERR_PARSING_LDAP_SYNTAX_OID); // We allow only unimplemented syntaxes to be substituted. if (schema.hasSyntax(oid)) @@ -2296,8 +2191,7 @@ * part of the ldapsyntaxes attribute. A virtual value is not searched and * hence never deleted. */ String oid = Schema.parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_LDAP_SYNTAX_OID); String oid = Schema.parseOID(definition, ERR_PARSING_LDAP_SYNTAX_OID); LDAPSyntaxDescription removeLSD = schema.getLdapSyntaxDescription(oid); if (removeLSD == null) @@ -2472,7 +2366,7 @@ // the same file are written before the subordinate rules. Set<DITStructureRule> addedDSRs = new HashSet<>(); values = new LinkedHashSet<>(); for (DITStructureRule dsr : schema.getDITStructureRulesByID().values()) for (DITStructureRule dsr : schema.getDITStructureRules()) { if (schemaFile.equals(getSchemaFile(dsr))) { opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -72,6 +72,7 @@ import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.CoreSchema; import org.forgerock.opendj.ldap.schema.DITContentRule; import org.forgerock.opendj.ldap.schema.DITStructureRule; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.NameForm; @@ -150,7 +151,6 @@ import org.opends.server.types.BackupConfig; import org.opends.server.types.Control; import org.opends.server.types.CryptoManager; import org.opends.server.types.DITStructureRule; import org.opends.server.types.DirectoryEnvironmentConfig; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; @@ -2631,32 +2631,6 @@ } /** * Retrieves the DIT structure rule associated with the provided name form. * * @param nameForm The name form for which to retrieve the associated DIT * structure rule. * * @return The requested DIT structure rule, or {@code null} if no such * rule is defined. */ public static DITStructureRule getDITStructureRule(NameForm nameForm) { return directoryServer.schema.getDITStructureRule(nameForm); } /** * Deregisters the provided DIT structure rule with the Directory Server. * * @param ditStructureRule The DIT structure rule to deregister with the * server. */ public static void deregisterDITStructureRule(DITStructureRule ditStructureRule) { directoryServer.schema.deregisterDITStructureRule(ditStructureRule); } /** * Retrieves the name forms associated with the specified objectclass. * * @param objectClass The objectclass for which to retrieve the associated opendj-server-legacy/src/main/java/org/opends/server/core/SchemaConfigManager.java
@@ -32,9 +32,7 @@ import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.CoreSchema; import org.forgerock.opendj.ldap.schema.Syntax; import org.opends.server.schema.DITStructureRuleSyntax; import org.opends.server.types.Attribute; import org.opends.server.types.DITStructureRule; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.InitializationException; @@ -47,8 +45,6 @@ import static org.opends.messages.ConfigMessages.*; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.schema.SchemaConstants.*; import static org.opends.server.types.CommonSchemaElements.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; /** @@ -720,9 +716,10 @@ // Register it with the schema. We will allow duplicates, with the // later definition overriding any earlier definition, but we want // to trap them and log a warning. String definition = v.toString(); try { schema.registerNameForm(v.toString(), schemaFile, failOnError); schema.registerNameForm(definition, schemaFile, failOnError); } catch (DirectoryException de) { @@ -732,7 +729,7 @@ try { schema.registerNameForm(v.toString(), schemaFile, true); schema.registerNameForm(definition, schemaFile, true); } catch (Exception e) { @@ -803,39 +800,13 @@ { for (ByteString v : a) { // Parse the DIT content rule. DITStructureRule dsr; try { dsr = DITStructureRuleSyntax.decodeDITStructureRule(v, schema, false); dsr.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME); setSchemaFile(dsr, schemaFile); } catch (DirectoryException de) { logger.traceException(de); LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_DSR.get( schemaFile, de.getMessageObject()); reportError(failOnError, de, message); continue; } catch (Exception e) { logger.traceException(e); LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_DSR.get( schemaFile, v + ": " + getExceptionMessage(e)); reportError(failOnError, e, message); continue; } // Register it with the schema. We will allow duplicates, with the // later definition overriding any earlier definition, but we want // to trap them and log a warning. String definition = v.toString(); try { schema.registerDITStructureRule(dsr, failOnError); schema.registerDITStructureRule(definition, schemaFile, failOnError); } catch (DirectoryException de) { @@ -845,7 +816,7 @@ try { schema.registerDITStructureRule(dsr, true); schema.registerDITStructureRule(definition, schemaFile, true); } catch (Exception e) { opendj-server-legacy/src/main/java/org/opends/server/extensions/GoverningStructureRuleVirtualAttributeProvider.java
@@ -26,7 +26,9 @@ import org.forgerock.opendj.ldap.RDN; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.DITStructureRule; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.server.config.server.GoverningStructureRuleVirtualAttributeCfg; import org.opends.server.api.VirtualAttributeProvider; import org.opends.server.core.DirectoryServer; @@ -34,9 +36,8 @@ import org.opends.server.types.AcceptRejectWarn; import org.opends.server.types.Attribute; import org.opends.server.types.Attributes; import org.opends.server.types.DITStructureRule; import org.opends.server.types.Entry; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.opends.server.types.Schema; import org.opends.server.types.VirtualAttributeRule; import static org.opends.messages.ExtensionMessages.*; @@ -177,7 +178,8 @@ if (oc == null) { return null; } Collection<NameForm> listForms = DirectoryServer.getNameForm(oc); Schema schema = DirectoryServer.getSchema(); Collection<NameForm> listForms = schema.getNameForm(oc); NameForm nameForm = null; DITStructureRule ditRule = null; //We iterate over all the nameforms while creating the entry and @@ -203,7 +205,11 @@ } if (nameForm != null && !obsolete) { ditRule = DirectoryServer.getDITStructureRule(nameForm); Collection<DITStructureRule> ditRules = schema.getDITStructureRules(nameForm); if (!ditRules.isEmpty()) { ditRule = ditRules.iterator().next(); } } } return ditRule; opendj-server-legacy/src/main/java/org/opends/server/schema/DITStructureRuleSyntax.java
@@ -15,26 +15,11 @@ * Portions Copyright 2011-2016 ForgeRock AS. */ package org.opends.server.schema; import static org.opends.messages.SchemaMessages.*; import static org.opends.server.schema.SchemaConstants.*; import static org.opends.server.util.StaticUtils.*; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.opendj.ldap.ByteSequence; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.opendj.server.config.server.AttributeSyntaxCfg; import org.opends.server.api.AttributeSyntax; import org.opends.server.core.DirectoryServer; import org.opends.server.types.DITStructureRule; import org.opends.server.types.DirectoryException; import org.opends.server.types.Schema; /** * This class implements the DIT structure rule description syntax, which is @@ -44,7 +29,6 @@ public class DITStructureRuleSyntax extends AttributeSyntax<AttributeSyntaxCfg> { /** * Creates a new instance of this syntax. Note that the only thing that * should be done here is to invoke the default constructor for the @@ -56,993 +40,27 @@ super(); } /** {@inheritDoc} */ @Override public Syntax getSDKSyntax(org.forgerock.opendj.ldap.schema.Schema schema) { return schema.getSyntax(SchemaConstants.SYNTAX_DIT_STRUCTURE_RULE_OID); } /** {@inheritDoc} */ @Override public String getName() { return SYNTAX_DIT_STRUCTURE_RULE_NAME; } /** {@inheritDoc} */ @Override public String getOID() { return SYNTAX_DIT_STRUCTURE_RULE_OID; } /** {@inheritDoc} */ @Override public String getDescription() { return SYNTAX_DIT_STRUCTURE_RULE_DESCRIPTION; } /** * Decodes the contents of the provided ASN.1 octet string as a DIT structure * rule definition according to the rules of this syntax. Note that the * provided octet string value does not need to be normalized (and in fact, it * should not be in order to allow the desired capitalization to be * preserved). * * @param value The ASN.1 octet string containing the value * to decode (it does not need to be * normalized). * @param schema The schema to use to resolve references to * other schema elements. * @param allowUnknownElements Indicates whether to allow values that * reference a name form and/or superior rules * which are not defined in the server schema. * This should only be true when called by * {@code valueIsAcceptable}. * * @return The decoded DIT structure rule definition. * * @throws DirectoryException If the provided value cannot be decoded as an * DIT structure rule definition. */ public static DITStructureRule decodeDITStructureRule(ByteSequence value, Schema schema, boolean allowUnknownElements) throws DirectoryException { // Get string representations of the provided value using the provided form // and with all lowercase characters. String valueStr = value.toString(); String lowerStr = toLowerCase(valueStr); // We'll do this a character at a time. First, skip over any leading // whitespace. int pos = 0; int length = valueStr.length(); while (pos < length && valueStr.charAt(pos) == ' ') { pos++; } if (pos >= length) { // This means that the value was empty or contained only whitespace. That // is illegal. LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_EMPTY_VALUE.get(); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be an open parenthesis. If it is not, then that // is an error. char c = valueStr.charAt(pos++); if (c != '(') { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_EXPECTED_OPEN_PARENTHESIS.get( valueStr, pos-1, c); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Skip over any spaces immediately following the opening parenthesis. while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { // This means that the end of the value was reached before we could find // the OID. Ths is illegal. LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next set of characters must be the rule ID, which is an integer. int ruleIDStartPos = pos; while (pos < length && ((c = valueStr.charAt(pos++)) != ' ')) { if (! isDigit(c)) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR_IN_RULE_ID.get( valueStr, c, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } // If we're at the end of the value, then it isn't a valid DIT structure // rule description. Otherwise, parse out the rule ID. int ruleID; if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { ruleID = Integer.parseInt(valueStr.substring(ruleIDStartPos, pos-1)); } // Skip over the space(s) after the rule ID. while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { // This means that the end of the value was reached before we could find // the rule ID. Ths is illegal. LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // At this point, we should have a pretty specific syntax that describes // what may come next, but some of the components are optional and it would // be pretty easy to put something in the wrong order, so we will be very // flexible about what we can accept. Just look at the next token, figure // out what it is and how to treat what comes after it, then repeat until // we get to the end of the value. But before we start, set default values // for everything else we might need to know. LinkedHashMap<String,String> names = new LinkedHashMap<>(); String description = null; boolean isObsolete = false; NameForm nameForm = null; boolean nameFormGiven = false; LinkedHashSet<DITStructureRule> superiorRules = null; LinkedHashMap<String,List<String>> extraProperties = new LinkedHashMap<>(); while (true) { StringBuilder tokenNameBuffer = new StringBuilder(); pos = readTokenName(valueStr, tokenNameBuffer, pos); String tokenName = tokenNameBuffer.toString(); String lowerTokenName = toLowerCase(tokenName); if (tokenName.equals(")")) { // We must be at the end of the value. If not, then that's a problem. if (pos < length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_UNEXPECTED_CLOSE_PARENTHESIS. get(valueStr, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } break; } else if (lowerTokenName.equals("name")) { // This specifies the set of names for the DIT structure rule. It may // be a single name in single quotes, or it may be an open parenthesis // followed by one or more names in single quotes separated by spaces. c = valueStr.charAt(pos++); if (c == '\'') { StringBuilder userBuffer = new StringBuilder(); StringBuilder lowerBuffer = new StringBuilder(); pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer, pos-1); names.put(lowerBuffer.toString(), userBuffer.toString()); } else if (c == '(') { StringBuilder userBuffer = new StringBuilder(); StringBuilder lowerBuffer = new StringBuilder(); pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer, pos); names.put(lowerBuffer.toString(), userBuffer.toString()); while (true) { if (valueStr.charAt(pos) == ')') { // Skip over any spaces after the parenthesis. pos++; while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } break; } else { userBuffer = new StringBuilder(); lowerBuffer = new StringBuilder(); pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer, pos); names.put(lowerBuffer.toString(), userBuffer.toString()); } } } else { // This is an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR.get(valueStr, c, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } else if (lowerTokenName.equals("desc")) { // This specifies the description for the DIT structure rule. It is an // arbitrary string of characters enclosed in single quotes. StringBuilder descriptionBuffer = new StringBuilder(); pos = readQuotedString(valueStr, descriptionBuffer, pos); description = descriptionBuffer.toString(); } else if (lowerTokenName.equals("obsolete")) { // This indicates whether the DIT structure rule should be considered // obsolete. We do not need to do any more parsing for this token. isObsolete = true; } else if (lowerTokenName.equals("form")) { // This should be the OID of the associated name form. StringBuilder woidBuffer = new StringBuilder(); pos = readWOID(lowerStr, woidBuffer, pos); nameFormGiven = true; String nameOrOid = woidBuffer.toString(); if (!schema.hasNameForm(nameOrOid) && !allowUnknownElements) { throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_ATTR_SYNTAX_DSR_UNKNOWN_NAME_FORM.get(valueStr, woidBuffer)); } nameForm = schema.getNameForm(nameOrOid); } else if (lowerTokenName.equals("sup")) { LinkedList<DITStructureRule> superiorList = new LinkedList<>(); // This specifies the set of superior rule IDs (which are integers) for // this DIT structure rule. It may be a single rule ID or a set of // rule IDs enclosed in parentheses and separated by spaces. c = valueStr.charAt(pos++); if (c == '(') { while (true) { // Skip over any leading spaces. while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read the next integer value. ruleIDStartPos = pos; while (pos < length && ((c = valueStr.charAt(pos++)) != ' ')) { if (! isDigit(c)) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR_IN_RULE_ID. get(valueStr, c, pos-1); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } // If we're at the end of the value, then it isn't a valid DIT // structure rule description. Otherwise, parse out the rule ID. int supRuleID; if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { supRuleID = Integer.parseInt(valueStr.substring(ruleIDStartPos, pos-1)); } // Get the DIT structure rule with the specified rule ID. DITStructureRule superiorRule = schema.getDITStructureRule(supRuleID); if (superiorRule == null) { if (! allowUnknownElements) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_UNKNOWN_RULE_ID.get( valueStr, supRuleID); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } else { superiorList.add(superiorRule); } // Skip over any trailing spaces. while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // If the next character is a closing parenthesis, then read any // spaces after it and break out of the loop. if (c == ')') { pos++; while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } break; } } } else { if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read the next integer value. ruleIDStartPos = pos - 1; while (pos < length && ((c = valueStr.charAt(pos++)) != ' ')) { if (! isDigit(c)) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR_IN_RULE_ID.get(valueStr, c, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } // If we're at the end of the value, then it isn't a valid DIT // structure rule description. Otherwise, parse out the rule ID. int supRuleID; if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { supRuleID = Integer.parseInt(valueStr.substring(ruleIDStartPos, pos-1)); } // Get the DIT structure rule with the specified rule ID. DITStructureRule superiorRule = schema.getDITStructureRule(supRuleID); if (superiorRule == null) { if (! allowUnknownElements) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_UNKNOWN_RULE_ID.get(valueStr, supRuleID); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } else { superiorList.add(superiorRule); } // Skip over any trailing spaces. while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } superiorRules = new LinkedHashSet<>(superiorList); } else { // This must be a non-standard property and it must be followed by // either a single value in single quotes or an open parenthesis // followed by one or more values in single quotes separated by spaces // followed by a close parenthesis. LinkedList<String> valueList = new LinkedList<>(); pos = readExtraParameterValues(valueStr, valueList, pos); extraProperties.put(tokenName, valueList); } } if (nameForm == null && !nameFormGiven) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_NO_NAME_FORM.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } return new DITStructureRule(value.toString(), names, ruleID, description, isObsolete, nameForm, superiorRules, extraProperties); } /** * Reads the next token name from the DIT content rule definition, skipping * over any leading or trailing spaces, and appends it to the provided buffer. * * @param valueStr The string representation of the DIT content rule * definition. * @param tokenName The buffer into which the token name will be written. * @param startPos The position in the provided string at which to start * reading the token name. * * @return The position of the first character that is not part of the token * name or one of the trailing spaces after it. * * @throws DirectoryException If a problem is encountered while reading the * token name. */ private static int readTokenName(String valueStr, StringBuilder tokenName, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = valueStr.length(); while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read until we find the next space. while (startPos < length && ((c = valueStr.charAt(startPos++)) != ' ')) { tokenName.append(c); } // Skip over any trailing spaces after the value. while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } // Return the position of the first non-space character after the token. return startPos; } /** * Reads the value of a string enclosed in single quotes, skipping over the * quotes and any leading or trailing spaces, and appending the string to the * provided buffer. * * @param valueStr The user-provided representation of the DIT content * rule definition. * @param valueBuffer The buffer into which the user-provided representation * of the value will be placed. * @param startPos The position in the provided string at which to start * reading the quoted string. * * @return The position of the first character that is not part of the quoted * string or one of the trailing spaces after it. * * @throws DirectoryException If a problem is encountered while reading the * quoted string. */ private static int readQuotedString(String valueStr, StringBuilder valueBuffer, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = valueStr.length(); while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be a single quote. if (c != '\'') { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_EXPECTED_QUOTE_AT_POS.get(valueStr, startPos, c); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read until we find the closing quote. startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) != '\'')) { valueBuffer.append(c); startPos++; } // Skip over any trailing spaces after the value. startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } // If we're at the end of the value, then that's illegal. if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Return the position of the first non-space character after the token. return startPos; } /** * Reads the value of a string enclosed in single quotes, skipping over the * quotes and any leading or trailing spaces, and appending the string to the * provided buffer. * * @param valueStr The user-provided representation of the DIT content * rule definition. * @param lowerStr The all-lowercase representation of the DIT content * rule definition. * @param userBuffer The buffer into which the user-provided representation * of the value will be placed. * @param lowerBuffer The buffer into which the all-lowercase representation * of the value will be placed. * @param startPos The position in the provided string at which to start * reading the quoted string. * * @return The position of the first character that is not part of the quoted * string or one of the trailing spaces after it. * * @throws DirectoryException If a problem is encountered while reading the * quoted string. */ private static int readQuotedString(String valueStr, String lowerStr, StringBuilder userBuffer, StringBuilder lowerBuffer, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = lowerStr.length(); while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be a single quote. if (c != '\'') { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_EXPECTED_QUOTE_AT_POS.get(valueStr, startPos, c); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read until we find the closing quote. startPos++; while (startPos < length && ((c = lowerStr.charAt(startPos)) != '\'')) { lowerBuffer.append(c); userBuffer.append(valueStr.charAt(startPos)); startPos++; } // Skip over any trailing spaces after the value. startPos++; while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } // If we're at the end of the value, then that's illegal. if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Return the position of the first non-space character after the token. return startPos; } /** * Reads an attributeType/objectclass description or numeric OID from the * provided string, skipping over any leading or trailing spaces, and * appending the value to the provided buffer. * * @param lowerStr The string from which the name or OID is to be read. * @param woidBuffer The buffer into which the name or OID should be * appended. * @param startPos The position at which to start reading. * * @return The position of the first character after the name or OID that is * not a space. * * @throws DirectoryException If a problem is encountered while reading the * name or OID. */ private static int readWOID(String lowerStr, StringBuilder woidBuffer, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = lowerStr.length(); while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be either numeric (for an OID) or alphabetic (for // an attribute type/objectclass description). if (isDigit(c)) { // This must be a numeric OID. In that case, we will accept only digits // and periods, but not consecutive periods. boolean lastWasPeriod = false; while (startPos < length && ((c = lowerStr.charAt(startPos++)) != ' ')) { if (c == '.') { if (lastWasPeriod) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_DOUBLE_PERIOD_IN_NUMERIC_OID. get(lowerStr, startPos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { woidBuffer.append(c); lastWasPeriod = true; } } else if (! isDigit(c)) { // Technically, this must be an illegal character. However, it is // possible that someone just got sloppy and did not include a space // between the name/OID and a closing parenthesis. In that case, // we'll assume it's the end of the value. What's more, we'll have // to prematurely return to nasty side effects from stripping off // additional characters. if (c == ')') { return startPos-1; } // This must have been an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR_IN_NUMERIC_OID.get(lowerStr, c, startPos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { woidBuffer.append(c); lastWasPeriod = false; } } } else if (isAlpha(c)) { // This must be an attribute type/objectclass description. In this case, // we will only accept alphabetic characters, numeric digits, and the // hyphen. while (startPos < length && ((c = lowerStr.charAt(startPos++)) != ' ')) { if (isAlpha(c) || isDigit(c) || c == '-' || (c == '_' && DirectoryServer.allowAttributeNameExceptions())) { woidBuffer.append(c); } else { // Technically, this must be an illegal character. However, it is // possible that someone just got sloppy and did not include a space // between the name/OID and a closing parenthesis. In that case, // we'll assume it's the end of the value. What's more, we'll have // to prematurely return to nasty side effects from stripping off // additional characters. if (c == ')') { return startPos-1; } // This must have been an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR_IN_STRING_OID.get(lowerStr, c, startPos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } else { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR.get(lowerStr, c, startPos); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Skip over any trailing spaces after the value. while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } // If we're at the end of the value, then that's illegal. if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Return the position of the first non-space character after the token. return startPos; } /** * Reads the value for an "extra" parameter. It will handle a single unquoted * word (which is technically illegal, but we'll allow it), a single quoted * string, or an open parenthesis followed by a space-delimited set of quoted * strings or unquoted words followed by a close parenthesis. * * @param valueStr The string containing the information to be read. * @param valueList The list of "extra" parameter values read so far. * @param startPos The position in the value string at which to start * reading. * * @return The "extra" parameter value that was read. * * @throws DirectoryException If a problem occurs while attempting to read * the value. */ private static int readExtraParameterValues(String valueStr, List<String> valueList, int startPos) throws DirectoryException { // Skip over any leading spaces. int length = valueStr.length(); char c = '\u0000'; while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Look at the next character. If it is a quote, then parse until the next // quote and end. If it is an open parenthesis, then parse individual // values until the close parenthesis and end. Otherwise, parse until the // next space and end. if (c == '\'') { // Parse until the closing quote. StringBuilder valueBuffer = new StringBuilder(); startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) != '\'')) { valueBuffer.append(c); startPos++; } startPos++; valueList.add(valueBuffer.toString()); } else if (c == '(') { startPos++; // We're expecting a list of values. Quoted, space separated. while (true) { // Skip over any leading spaces; while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } if (c == ')') { // This is the end of the list. startPos++; break; } else if (c == '(') { // This is an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_ILLEGAL_CHAR.get(valueStr, c, startPos); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else if (c == '\'') { // We have a quoted string StringBuilder valueBuffer = new StringBuilder(); startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) != '\'')) { valueBuffer.append(c); startPos++; } valueList.add(valueBuffer.toString()); startPos++; } else { //Consider unquoted string StringBuilder valueBuffer = new StringBuilder(); while (startPos < length && ((c = valueStr.charAt(startPos)) != ' ')) { valueBuffer.append(c); startPos++; } valueList.add(valueBuffer.toString()); } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } else { // Parse until the next space. StringBuilder valueBuffer = new StringBuilder(); while (startPos < length && ((c = valueStr.charAt(startPos)) != ' ')) { valueBuffer.append(c); startPos++; } valueList.add(valueBuffer.toString()); } // Skip over any trailing spaces. while (startPos < length && valueStr.charAt(startPos) == ' ') { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } return startPos; } } opendj-server-legacy/src/main/java/org/opends/server/types/DITStructureRule.java
File was deleted opendj-server-legacy/src/main/java/org/opends/server/types/DirectoryConfig.java
@@ -24,6 +24,7 @@ import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.DITContentRule; import org.forgerock.opendj.ldap.schema.DITStructureRule; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.NameForm; @@ -382,22 +383,6 @@ } /** * Retrieves the DIT structure rule associated with the provided * name form. * * @param nameForm The name form for which to retrieve the * associated DIT structure rule. * * @return The requested DIT structure rule, or <CODE>null</CODE> * if no such rule is defined. */ public static DITStructureRule getDITStructureRule(NameForm nameForm) { return DirectoryServer.getDITStructureRule(nameForm); } /** * Retrieves the collection of name forms associated with the specified * structural objectclass. * opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
@@ -47,6 +47,7 @@ import org.forgerock.opendj.ldap.SearchScope; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.DITContentRule; import org.forgerock.opendj.ldap.schema.DITStructureRule; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; @@ -1652,10 +1653,13 @@ if (validateStructureRules && nameForm != null) { ditStructureRule = DirectoryServer.getDITStructureRule(nameForm); if (ditStructureRule != null && ditStructureRule.isObsolete()) for (DITStructureRule ditRule : DirectoryServer.getSchema().getDITStructureRules(nameForm)) { ditStructureRule = null; if (!ditRule.isObsolete()) { ditStructureRule = ditRule; break; } } } } @@ -2002,7 +2006,7 @@ { // If there is a DIT structure rule for this entry, then make sure // that the entry is in compliance with it. if (ditStructureRule != null && ditStructureRule.hasSuperiorRules()) if (ditStructureRule != null && !ditStructureRule.getSuperiorRules().isEmpty()) { if (parentProvided) { @@ -2166,23 +2170,22 @@ { for(NameForm parentNF : allNFs) { if (parentNF != null && !parentNF.isObsolete()) if (!parentNF.isObsolete()) { DITStructureRule parentDSR = DirectoryServer.getDITStructureRule(parentNF); if (parentDSR != null && !parentDSR.isObsolete()) for (DITStructureRule parentDSR : DirectoryServer.getSchema().getDITStructureRules(parentNF)) { LocalizableMessage message = ERR_ENTRY_SCHEMA_VIOLATES_PARENT_DSR.get(dn, parentEntry.getName()); if (structuralPolicy == AcceptRejectWarn.REJECT) if (!parentDSR.isObsolete()) { invalidReason.append(message); return false; } else if (structuralPolicy == AcceptRejectWarn.WARN) { logger.error(message); LocalizableMessage message = ERR_ENTRY_SCHEMA_VIOLATES_PARENT_DSR.get(dn, parentEntry.getName()); if (structuralPolicy == AcceptRejectWarn.REJECT) { invalidReason.append(message); return false; } else if (structuralPolicy == AcceptRejectWarn.WARN) { logger.error(message); } } } } @@ -2242,7 +2245,7 @@ boolean matchFound = false; for (DITStructureRule dsr2 : dsr.getSuperiorRules()) { if (dsr2.getStructuralClass().equals(oc)) if (dsr2.getNameForm().getStructuralClass().equals(oc)) { matchFound = true; } opendj-server-legacy/src/main/java/org/opends/server/types/Schema.java
@@ -50,6 +50,7 @@ import org.forgerock.opendj.ldap.schema.ConflictingSchemaElementException; import org.forgerock.opendj.ldap.schema.CoreSchema; import org.forgerock.opendj.ldap.schema.DITContentRule; import org.forgerock.opendj.ldap.schema.DITStructureRule; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.MatchingRuleUse.Builder; @@ -63,16 +64,13 @@ import org.forgerock.util.Utils; import org.opends.server.core.DirectoryServer; import org.opends.server.core.SchemaConfigManager; import org.opends.server.schema.DITStructureRuleSyntax; import org.opends.server.util.Base64; import org.opends.server.util.ServerConstants; import org.opends.server.util.StaticUtils; import static org.opends.messages.BackendMessages.*; import static org.opends.messages.CoreMessages.*; import static org.opends.messages.SchemaMessages.*; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.types.CommonSchemaElements.*; import static org.opends.server.util.CollectionUtils.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; @@ -110,20 +108,6 @@ private Map<AttributeType, List<AttributeType>> subordinateTypes; /** * The set of DIT structure rules for this schema, mapped between the name * form for the definition and the DIT structure rule itself. */ private ConcurrentHashMap<Integer,DITStructureRule> ditStructureRulesByID; /** * The set of DIT structure rules for this schema, mapped between the name * form for the definition and the DIT structure rule itself. */ private ConcurrentHashMap<NameForm,DITStructureRule> ditStructureRulesByNameForm; /** * The set of ldap syntax descriptions for this schema, mapped the OID and the * ldap syntax description itself. */ @@ -168,8 +152,6 @@ { switchSchema(schemaNG); ditStructureRulesByID = new ConcurrentHashMap<Integer,DITStructureRule>(); ditStructureRulesByNameForm = new ConcurrentHashMap<NameForm,DITStructureRule>(); ldapSyntaxDescriptions = new ConcurrentHashMap<String,LDAPSyntaxDescription>(); subordinateTypes = new ConcurrentHashMap<AttributeType,List<AttributeType>>(); @@ -405,6 +387,36 @@ } /** * Parses a DIT structure rule from its provided definition. * * @param definition * The definition of the DIT structure rule * @return the DIT structure rule * @throws DirectoryException * If an error occurs */ public DITStructureRule parseDITStructureRule(String definition) throws DirectoryException { try { SchemaBuilder builder = new SchemaBuilder(schemaNG); builder.addDITStructureRule(definition, true); org.forgerock.opendj.ldap.schema.Schema newSchema = builder.toSchema(); rejectSchemaWithWarnings(newSchema); return newSchema.getDITStructureRule(parseRuleID(definition)); } catch (UnknownSchemaElementException e) { LocalizableMessage msg = ERR_NAME_FORM_CANNOT_REGISTER.get(definition); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, msg, e); } catch (LocalizedIllegalArgumentException e) { throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, e.getMessageObject(), e); } } /** * Registers a list of attribute types from their provided definitions. * <p> * This method allows to do only one schema change for multiple definitions, @@ -595,27 +607,33 @@ */ public String parseObjectClassOID(String definition) throws DirectoryException { return parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_OBJECTCLASS_OID); return parseOID(definition, ERR_PARSING_OBJECTCLASS_OID); } private String parseAttributeTypeOID(String definition) throws DirectoryException { return parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_ATTRIBUTE_TYPE_OID); return parseOID(definition, ERR_PARSING_ATTRIBUTE_TYPE_OID); } private String parseMatchingRuleUseOID(String definition) throws DirectoryException { return parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_MATCHING_RULE_USE_OID); return parseOID(definition, ERR_PARSING_MATCHING_RULE_USE_OID); } private String parseNameFormOID(String definition) throws DirectoryException { return parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_NAME_FORM_OID); return parseOID(definition, ERR_PARSING_NAME_FORM_OID); } private String parseDITContentRuleOID(String definition) throws DirectoryException { return parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_DIT_CONTENT_RULE_OID); return parseOID(definition, ERR_PARSING_DIT_CONTENT_RULE_OID); } private int parseRuleID(String definition) throws DirectoryException { // Reuse code of parseOID, even though this is not an OID return Integer.parseInt(parseOID(definition, ERR_PARSING_DIT_STRUCTURE_RULE_RULEID)); } /** @@ -626,14 +644,12 @@ * * @param definition * The definition of a schema element, assumed to be valid * @param parsingErrorResultCode the result code to use if a problem occurs while parsing the definition * @param parsingErrorMsg the message to use if a problem occurs while parsing the definition * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static String parseOID(String definition, ResultCode parsingErrorResultCode, Arg1<Object> parsingErrorMsg) throws DirectoryException public static String parseOID(String definition, Arg1<Object> parsingErrorMsg) throws DirectoryException { try { @@ -661,7 +677,7 @@ } catch (IndexOutOfBoundsException e) { throw new DirectoryException(parsingErrorResultCode, parsingErrorMsg.get(definition), e); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, parsingErrorMsg.get(definition), e); } } @@ -1608,39 +1624,16 @@ /** * Retrieves the DIT structure rule definitions for this schema, as * a mapping between the rule ID for the rule and the DIT structure * rule itself. Each DIT structure rule should only be present * once, since its only key is its rule ID. The contents of the * returned mapping must not be altered. * Retrieves the DIT structure rule definitions for this schema. * The contents of the returned mapping must not be altered. * * @return The DIT structure rule definitions for this schema. * @return The DIT structure rule definitions for this schema. */ public ConcurrentHashMap<Integer,DITStructureRule> getDITStructureRulesByID() public Collection<DITStructureRule> getDITStructureRules() { return ditStructureRulesByID; return schemaNG.getDITStuctureRules(); } /** * Retrieves the DIT structure rule definitions for this schema, as * a mapping between the name form for the rule and the DIT * structure rule itself. Each DIT structure rule should only be * present once, since its only key is its name form. The contents * of the returned mapping must not be altered. * * @return The DIT structure rule definitions for this schema. */ public ConcurrentHashMap<NameForm,DITStructureRule> getDITStructureRulesByNameForm() { return ditStructureRulesByNameForm; } /** * Retrieves the DIT structure rule definition with the provided * rule ID. @@ -1654,106 +1647,123 @@ */ public DITStructureRule getDITStructureRule(int ruleID) { return ditStructureRulesByID.get(ruleID); return schemaNG.getDITStructureRule(ruleID); } /** * Retrieves the DIT structure rule definitions for the provided name form. * * @param nameForm * The name form for the DIT structure rule to retrieve. * @return The requested DIT structure rules, or {@code null} if no DIT structure rule is * registered with the provided name form. */ public Collection<DITStructureRule> getDITStructureRules(NameForm nameForm) { return schemaNG.getDITStructureRules(nameForm); } /** * Retrieves the DIT structure rule definition for the provided name * form. * Registers the provided DIT structure rule definition with this schema. * * @param nameForm The name form for the DIT structure rule to * retrieve. * * @return The requested DIT structure rule, or <CODE>null</CODE> * if no DIT structure rule is registered with the provided * name form. * @param ditStructureRule * The DIT structure rule to register. * @param schemaFile * The schema file where this definition belongs, maybe {@code null} * @param overwriteExisting * Indicates whether to overwrite an existing mapping if there are any conflicts (i.e., * another DIT structure rule with the same name form). * @throws DirectoryException * If a conflict is encountered and the {@code overwriteExisting} flag is set to * {@code false} */ public DITStructureRule getDITStructureRule(NameForm nameForm) public void registerDITStructureRule(DITStructureRule ditStructureRule, String schemaFile, boolean overwriteExisting) throws DirectoryException { return ditStructureRulesByNameForm.get(nameForm); } /** * Registers the provided DIT structure rule definition with this * schema. * * @param ditStructureRule The DIT structure rule to register. * @param overwriteExisting Indicates whether to overwrite an * existing mapping if there are any * conflicts (i.e., another DIT structure * rule with the same name form). * * @throws DirectoryException If a conflict is encountered and the * <CODE>overwriteExisting</CODE> flag * is set to <CODE>false</CODE> */ public void registerDITStructureRule( DITStructureRule ditStructureRule, boolean overwriteExisting) throws DirectoryException { synchronized (ditStructureRulesByNameForm) exclusiveLock.lock(); try { NameForm nameForm = ditStructureRule.getNameForm(); int ruleID = ditStructureRule.getRuleID(); if (! overwriteExisting) SchemaBuilder builder = new SchemaBuilder(schemaNG); DITStructureRule.Builder dsrBuilder = builder.buildDITStructureRule(ditStructureRule); if (schemaFile != null) { if (ditStructureRulesByNameForm.containsKey(nameForm)) { DITStructureRule conflictingRule = ditStructureRulesByNameForm.get(nameForm); LocalizableMessage message = ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_NAME_FORM. get(ditStructureRule.getNameOrRuleID(), nameForm.getNameOrOID(), conflictingRule.getNameOrRuleID()); throw new DirectoryException( ResultCode.CONSTRAINT_VIOLATION, message); } if (ditStructureRulesByID.containsKey(ruleID)) { DITStructureRule conflictingRule = ditStructureRulesByID.get(ruleID); LocalizableMessage message = ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_ID. get(ditStructureRule.getNameOrRuleID(), ruleID, conflictingRule.getNameOrRuleID()); throw new DirectoryException( ResultCode.CONSTRAINT_VIOLATION, message); } dsrBuilder.removeExtraProperty(SCHEMA_PROPERTY_FILENAME) .extraProperties(SCHEMA_PROPERTY_FILENAME, schemaFile); } ditStructureRulesByNameForm.put(nameForm, ditStructureRule); ditStructureRulesByID.put(ruleID, ditStructureRule); if (overwriteExisting) { dsrBuilder.addToSchemaOverwrite(); } else { dsrBuilder.addToSchema(); } switchSchema(builder.toSchema()); } catch (LocalizedIllegalArgumentException e) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, e.getMessageObject(), e); } finally { exclusiveLock.unlock(); } } /** * Registers the provided DIT structure rule definition with this schema. * * @param definition * The definition of the DIT structure rule to register. * @param schemaFile * The schema file where this definition belongs, maybe {@code null} * @param overwriteExisting * Indicates whether to overwrite an existing mapping if there are any conflicts * (i.e., another DIT structure rule with the same name form). * @throws DirectoryException * If a conflict is encountered and the {@code overwriteExisting} flag is set to * {@code false} */ public void registerDITStructureRule(String definition, String schemaFile, boolean overwriteExisting) throws DirectoryException { exclusiveLock.lock(); try { String definitionWithFile = getDefinitionWithSchemaFile(definition, schemaFile); switchSchema(new SchemaBuilder(schemaNG) .addDITStructureRule(definitionWithFile, overwriteExisting) .toSchema()); } finally { exclusiveLock.unlock(); } } /** * Deregisters the provided DIT structure rule definition with this * schema. * Deregisters the provided DIT structure rule definition with this schema. * * @param ditStructureRule The DIT structure rule to deregister * with this schema. * @param ditStructureRule * The DIT structure rule to deregister with this schema. * @throws DirectoryException * If an error occurs. */ public void deregisterDITStructureRule( DITStructureRule ditStructureRule) DITStructureRule ditStructureRule) throws DirectoryException { synchronized (ditStructureRulesByNameForm) exclusiveLock.lock(); try { ditStructureRulesByNameForm.remove( ditStructureRule.getNameForm(), ditStructureRule); ditStructureRulesByID.remove(ditStructureRule.getRuleID(), ditStructureRule); SchemaBuilder builder = new SchemaBuilder(schemaNG); builder.removeDITStructureRule(ditStructureRule.getRuleID()); switchSchema(builder.toSchema()); } finally { exclusiveLock.unlock(); } } @@ -2205,16 +2215,6 @@ return values != null && ! values.isEmpty() ? values.get(0) : null; } private DITStructureRule recreateFromDefinition(DITStructureRule dsr) throws DirectoryException { ByteString value = ByteString.valueOfUtf8(dsr.toString()); DITStructureRule copy = DITStructureRuleSyntax.decodeDITStructureRule(value, this, false); setSchemaFile(copy, getSchemaFile(dsr)); return copy; } /** * Creates a new {@link Schema} object that is a duplicate of this one. It elements may be added * and removed from the duplicate without impacting this version. @@ -2235,8 +2235,6 @@ } dupSchema.subordinateTypes.putAll(subordinateTypes); dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID); dupSchema.ditStructureRulesByNameForm.putAll(ditStructureRulesByNameForm); dupSchema.ldapSyntaxDescriptions.putAll(ldapSyntaxDescriptions); dupSchema.oldestModificationTime = oldestModificationTime; dupSchema.youngestModificationTime = youngestModificationTime; @@ -2665,18 +2663,6 @@ schemaNG = null; } if (ditStructureRulesByID != null) { ditStructureRulesByID.clear(); ditStructureRulesByID = null; } if (ditStructureRulesByNameForm != null) { ditStructureRulesByNameForm.clear(); ditStructureRulesByNameForm = null; } if (subordinateTypes != null) { subordinateTypes.clear(); opendj-server-legacy/src/messages/org/opends/messages/schema.properties
@@ -518,3 +518,5 @@ of name form: '%s' ERR_PARSING_DIT_CONTENT_RULE_OID_351=Unable to parse the OID from the provided definition \ of DIT content rule: '%s' ERR_PARSING_DIT_STRUCTURE_RULE_RULEID_352=Unable to parse the rule ID \ from the provided definition of DIT structure rule: '%s' opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java
@@ -46,6 +46,7 @@ import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldap.schema.SchemaBuilder; import org.forgerock.opendj.ldap.schema.UnknownSchemaElementException; import org.forgerock.util.Utils; import org.opends.server.TestCaseUtils; import org.opends.server.core.AddOperation; @@ -3181,8 +3182,7 @@ * @throws Exception If an unexpected problem occurs. */ @Test public void testAddDITStructureRuleSuccessful() throws Exception public void testAddDITStructureRuleSuccessful() throws Exception { String ldif = toLdif( "dn: cn=schema", @@ -3207,13 +3207,49 @@ int ruleID = 999001; assertSchemaHasDITStructureRule(ruleID, false); runModify(argsNotPermissive(), ldif, System.err, SUCCESS); assertSchemaHasDITStructureRule(ruleID, true); try { runModify(argsNotPermissive(), ldif, System.err, SUCCESS); assertSchemaHasDITStructureRule(ruleID, true); } finally { // delete in reverse order String ldif2 = toLdif( "dn: cn=schema", "changetype: modify", "delete: ditStructureRules", "ditStructureRules: ( 999001 " + "NAME 'testAddDITStructureRuleSuccessful' " + "FORM testAddDITStructureRuleSuccessfulNF " + "X-ORIGIN 'SchemaBackendTestCase' )", "-", "delete: nameForms", "nameForms: ( testaddditstructurerulesuccessfulnf-oid " + "NAME 'testAddDITStructureRuleSuccessfulNF' " + "OC testAddDITStructureRuleSuccessfulOC MUST cn " + "X-ORIGIN 'SchemaBackendTestCase' )", "-", "delete: objectClasses", "objectClasses: ( testaddditstructurerulesuccessfuloc-oid " + "NAME 'testAddDITStructureRuleSuccessfulOC' SUP top " + "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')"); runModify(argsNotPermissive(), ldif2, System.err, SUCCESS); assertSchemaHasDITStructureRule(ruleID, false); } } private void assertSchemaHasDITStructureRule(int ruleID, boolean expected) { assertEquals(DirectoryServer.getSchema().getDITStructureRulesByID().containsKey(ruleID), expected); try { DirectoryServer.getSchema().getDITStructureRule(ruleID); assertTrue(expected, "Expected to find a DITStructureRule with ruleID " + ruleID); } catch (UnknownSchemaElementException e) { assertFalse(expected, e.getMessage()); } } /** @@ -3383,7 +3419,7 @@ int ruleID = 999004; assertSchemaHasDITStructureRule(ruleID, false); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertSchemaHasDITStructureRule(ruleID, false); } @@ -3420,11 +3456,51 @@ int ruleID = 999005; assertSchemaHasDITStructureRule(ruleID, false); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertSchemaHasDITStructureRule(ruleID, false); } /** * Tests the addition of a new DITContentRule with a conflicting rule identifier. * * @throws Exception * If an unexpected problem occurs. */ @Test public void testAddDITStructureRuleConflictingRuleID() throws Exception { String ldif = toLdif( "dn: cn=schema", "changetype: modify", "add: nameForms", "nameForms: ( 1.3.6.1.1.10.15.100 NAME 'domainNameForm' OC domain MUST ( dc ) )", "-", "add: dITStructureRules", "dITStructureRules: ( 1 NAME 'dummyStructureRule' FORM domainNameForm )"); runModify(argsNotPermissive(), ldif, ATTRIBUTE_OR_VALUE_EXISTS); } /** * Tests the addition of a new DITContentRule with a conflicting rule identifier. * * @throws Exception * If an unexpected problem occurs. */ @Test public void testAddDITStructureRuleConflictingRuleIDWithPermissiveControl() throws Exception { String ldif = toLdif( "dn: cn=schema", "changetype: modify", "add: nameForms", "nameForms: ( 1.3.6.1.1.10.15.100 NAME 'domainNameForm' OC domain MUST ( dc ) )", "-", "add: dITStructureRules", "dITStructureRules: ( 1 NAME 'dummyStructureRule' FORM domainNameForm )"); runModify(argsPermissive(), ldif, UNWILLING_TO_PERFORM); } /** * Tests the behavior of the schema backend when attempting to add a new * DIT structure rule that references a name form which is OBSOLETE. * opendj-server-legacy/src/test/java/org/opends/server/types/DITStructureRuleTestCase.java
File was deleted