mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Yannick Lecaillez
23.59.2016 160491be597f55e22b5d5a11bd0c7c1c6a440104
OPENDJ-3157: Schema updates via LDAP can overwrite existing schema.

The X-SCHEMA-FILE property were not set on elements when loading the
schema. This caused all the previous content of rewritten files to be
discarded.
2 files modified
219 ■■■■■ changed files
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java 166 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/SchemaConfigManager.java 53 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -122,6 +122,73 @@
        return subschemaDN;
    }
    /** Allows to perform modifications on element's builders before adding the result to this schema builder. */
    public interface SchemaBuilderHook {
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddSyntax(Syntax.Builder builder);
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddAttribute(AttributeType.Builder builder);
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddObjectClass(ObjectClass.Builder builder);
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddMatchingRuleUse(MatchingRuleUse.Builder builder);
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddMatchingRule(MatchingRule.Builder builder);
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddDitContentRule(DITContentRule.Builder builder);
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddDitStructureRule(DITStructureRule.Builder builder);
        /**
         * Allow to modify the builder before its inclusion in schema.
         *
         * @param builder
         *            Schema's element builder.
         */
        public void beforeAddNameForm(NameForm.Builder builder);
    }
    private Map<Integer, DITStructureRule> id2StructureRules;
    private Map<String, List<AttributeType>> name2AttributeTypes;
    private Map<String, List<DITContentRule>> name2ContentRules;
@@ -167,7 +234,7 @@
     */
    public SchemaBuilder(final Entry entry) {
        preLazyInitBuilder(entry.getName().toString(), null);
        addSchema(entry, true);
        addSchema(entry, true, null);
    }
    /**
@@ -218,6 +285,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addAttributeType(final String definition, final boolean overwrite) {
        return addAttributeType(definition, overwrite, null);
    }
    SchemaBuilder addAttributeType(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -371,6 +442,9 @@
            atBuilder.superiorType(superiorType)
                     .syntax(syntax);
            if (hook != null) {
                hook.beforeAddAttribute(atBuilder);
            }
            return atBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg = ERR_ATTR_SYNTAX_ATTRTYPE_INVALID1.get(definition, e.getMessageObject());
@@ -397,6 +471,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addDITContentRule(final String definition, final boolean overwrite) {
        return addDITContentRule(definition, overwrite, null);
    }
    SchemaBuilder addDITContentRule(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -478,6 +556,9 @@
                }
            }
            if (hook != null) {
                hook.beforeAddDitContentRule(contentRuleBuilder);
            }
            return contentRuleBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg = ERR_ATTR_SYNTAX_DCR_INVALID1.get(definition, e.getMessageObject());
@@ -504,6 +585,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addDITStructureRule(final String definition, final boolean overwrite) {
        return addDITStructureRule(definition, overwrite, null);
    }
    SchemaBuilder addDITStructureRule(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -585,6 +670,9 @@
            }
            ruleBuilder.nameForm(nameForm);
            if (hook != null) {
                hook.beforeAddDitStructureRule(ruleBuilder);
            }
            return ruleBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg = ERR_ATTR_SYNTAX_DSR_INVALID1.get(definition, e.getMessageObject());
@@ -637,6 +725,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addMatchingRule(final String definition, final boolean overwrite) {
        return addMatchingRule(definition, overwrite, null);
    }
    SchemaBuilder addMatchingRule(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -720,6 +812,9 @@
            if (syntax == null) {
                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_MR_NO_SYNTAX.get(definition));
            }
            if (hook != null) {
                hook.beforeAddMatchingRule(matchingRuleBuilder);
            }
            matchingRuleBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg =
@@ -748,6 +843,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addMatchingRuleUse(final String definition, final boolean overwrite) {
        return addMatchingRuleUse(definition, overwrite, null);
    }
    SchemaBuilder addMatchingRuleUse(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -829,6 +928,9 @@
            }
            useBuilder.attributes(attributes);
            if (hook != null) {
                hook.beforeAddMatchingRuleUse(useBuilder);
            }
            return useBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg = ERR_ATTR_SYNTAX_MRUSE_INVALID1.get(definition, e.getMessageObject());
@@ -929,6 +1031,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addNameForm(final String definition, final boolean overwrite) {
        return addNameForm(definition, overwrite, null);
    }
    SchemaBuilder addNameForm(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -1027,6 +1133,9 @@
                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_NAME_FORM_NO_REQUIRED_ATTR.get(definition));
            }
            if (hook != null) {
                hook.beforeAddNameForm(nameFormBuilder);
            }
            nameFormBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg =
@@ -1246,6 +1355,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addObjectClass(final String definition, final boolean overwrite) {
        return addObjectClass(definition, overwrite, null);
    }
    SchemaBuilder addObjectClass(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -1335,6 +1448,9 @@
                        ERR_ATTR_SYNTAX_OBJECTCLASS_ILLEGAL_TOKEN1.get(definition, tokenName));
                }
            }
            if (hook != null) {
                hook.beforeAddObjectClass(ocBuilder);
            }
            if (EXTENSIBLE_OBJECT_OBJECTCLASS_OID.equals(oid)) {
                addObjectClass(newExtensibleObjectObjectClass(
@@ -1411,7 +1527,7 @@
        // The call to addSchema will perform copyOnWrite.
        final SearchRequest request = getReadSchemaSearchRequest(name);
        final Entry entry = connection.searchSingleEntry(request);
        return addSchema(entry, overwrite);
        return addSchema(entry, overwrite, null);
    }
    /**
@@ -1430,6 +1546,27 @@
     *             If {@code entry} was {@code null}.
     */
    public SchemaBuilder addSchema(final Entry entry, final boolean overwrite) {
        return addSchema(entry, overwrite, null);
    }
    /**
     * 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
     * {@link Schema#getWarnings()} method.
     *
     * @param entry
     *            The subschema subentry to be parsed.
     * @param overwrite
     *            {@code true} if existing schema elements with the same
     *            conflicting OIDs should be overwritten.
     * @param hook
     *            Allows to perform modifications on element's builders before adding the result to this schema builder.
     * @return A reference to this schema builder.
     * @throws NullPointerException
     *             If {@code entry} was {@code null}.
     */
    public SchemaBuilder addSchema(final Entry entry, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(entry);
        lazyInitBuilder();
@@ -1438,7 +1575,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addSyntax(def.toString(), overwrite);
                    addSyntax(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1449,7 +1586,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addAttributeType(def.toString(), overwrite);
                    addAttributeType(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1460,7 +1597,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addObjectClass(def.toString(), overwrite);
                    addObjectClass(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1471,7 +1608,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addMatchingRuleUse(def.toString(), overwrite);
                    addMatchingRuleUse(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1482,7 +1619,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addMatchingRule(def.toString(), overwrite);
                    addMatchingRule(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1493,7 +1630,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addDITContentRule(def.toString(), overwrite);
                    addDITContentRule(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1504,7 +1641,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addDITStructureRule(def.toString(), overwrite);
                    addDITStructureRule(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1515,7 +1652,7 @@
        if (attr != null) {
            for (final ByteString def : attr) {
                try {
                    addNameForm(def.toString(), overwrite);
                    addNameForm(def.toString(), overwrite, hook);
                } catch (final LocalizedIllegalArgumentException e) {
                    warnings.add(e.getMessageObject());
                }
@@ -1582,7 +1719,7 @@
                new Function<SearchResultEntry, SchemaBuilder, LdapException>() {
                    @Override
                    public SchemaBuilder apply(SearchResultEntry result) throws LdapException {
                        addSchema(result, overwrite);
                        addSchema(result, overwrite, null);
                        return SchemaBuilder.this;
                    }
                });
@@ -1718,6 +1855,10 @@
     *             If {@code definition} was {@code null}.
     */
    public SchemaBuilder addSyntax(final String definition, final boolean overwrite) {
        return addSyntax(definition, overwrite, null);
    }
    SchemaBuilder addSyntax(final String definition, final boolean overwrite, SchemaBuilderHook hook) {
        Reject.ifNull(definition);
        lazyInitBuilder();
@@ -1784,6 +1925,9 @@
                }
            }
            if (hook != null) {
                hook.beforeAddSyntax(syntaxBuilder);
            }
            syntaxBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg =
opendj-server-legacy/src/main/java/org/opends/server/core/SchemaConfigManager.java
@@ -36,7 +36,16 @@
import org.forgerock.opendj.ldap.ResultCode;
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;
import org.forgerock.opendj.ldap.schema.ObjectClass;
import org.forgerock.opendj.ldap.schema.SchemaBuilder;
import org.forgerock.opendj.ldap.schema.SchemaBuilder.SchemaBuilderHook;
import org.forgerock.opendj.ldap.schema.Syntax;
import org.forgerock.opendj.ldap.schema.AttributeType.Builder;
import org.forgerock.opendj.ldif.LDIFEntryReader;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
@@ -47,6 +56,7 @@
import static org.forgerock.opendj.ldap.schema.SchemaValidationPolicy.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.ServerConstants.SCHEMA_PROPERTY_FILENAME;
/**
 * This class defines a utility that will be used to manage the interaction with
@@ -357,7 +367,7 @@
      // immediately overwrite these definitions which are already defined in the SDK core schema
      final boolean overwriteCoreSchemaDefinitions =
          CORE_SCHEMA_FILE.equals(schemaFile) || RFC_3112_SCHEMA_FILE.equals(schemaFile);
      updateSchema(schema, schemaEntry, overwriteCoreSchemaDefinitions);
      updateSchema(schema, schemaFile, schemaEntry, overwriteCoreSchemaDefinitions);
    }
    catch (DirectoryException e)
    {
@@ -369,7 +379,7 @@
        logger.warn(WARN_CONFIG_CONFLICTING_DEFINITIONS_IN_SCHEMA_FILE, schemaFile, e.getMessageObject());
        try
        {
          updateSchema(schema, schemaEntry, true);
          updateSchema(schema, schemaFile, schemaEntry, true);
        }
        catch (DirectoryException e2)
        {
@@ -439,15 +449,48 @@
    }
  }
  private static void updateSchema(Schema schema, final Entry schemaEntry, final boolean overwrite)
      throws DirectoryException
    private static void updateSchema(Schema schema, final String schemaFile, final Entry schemaEntry,
            final boolean overwrite) throws DirectoryException
  {
    schema.updateSchema(new SchemaUpdater()
    {
      @Override
      public org.forgerock.opendj.ldap.schema.Schema update(SchemaBuilder builder)
      {
        return builder.addSchema(schemaEntry, overwrite).toSchema();
        return builder.addSchema(schemaEntry, overwrite, new SchemaBuilderHook() {
            @Override
            public void beforeAddSyntax(Syntax.Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
            @Override
            public void beforeAddObjectClass(ObjectClass.Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
            @Override
            public void beforeAddNameForm(NameForm.Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
            @Override
            public void beforeAddMatchingRuleUse(MatchingRuleUse.Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
            @Override
            public void beforeAddMatchingRule(MatchingRule.Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
            @Override
            public void beforeAddDitStructureRule(DITStructureRule.Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
            @Override
            public void beforeAddDitContentRule(DITContentRule.Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
            @Override
            public void beforeAddAttribute(Builder builder) {
                builder.extraProperties(SCHEMA_PROPERTY_FILENAME, Collections.singletonList(schemaFile));
            }
        }).toSchema();
      }
    });
  }