From 270c01b95c4b0208f65d9a3a2d3e9ac50b06a76b Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 06 Oct 2016 09:12:10 +0000
Subject: [PATCH] OPENDJ-2860: support JSON syntaxes and matching rules in the server
---
opendj-server-legacy/src/main/java/org/opends/server/schema/CoreSchemaProvider.java | 17 ++
opendj-server-legacy/src/main/java/org/opends/server/schema/JsonSchemaProvider.java | 152 +++++++++++++++++++
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CoreSchemaConfiguration.xml | 40 ++++
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JsonSchemaConfiguration.xml | 160 ++++++++++++++++++++
opendj-server-legacy/resource/schema/02-config.ldif | 45 +++++
opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaHandler.java | 8
6 files changed, 416 insertions(+), 6 deletions(-)
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CoreSchemaConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CoreSchemaConfiguration.xml
index 442f3f3..2490c42 100644
--- a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CoreSchemaConfiguration.xml
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CoreSchemaConfiguration.xml
@@ -18,8 +18,7 @@
extends="schema-provider"
package="org.forgerock.opendj.server.config"
xmlns:adm="http://opendj.forgerock.org/admin"
- xmlns:ldap="http://opendj.forgerock.org/admin-ldap"
- xmlns:cli="http://opendj.forgerock.org/admin-cli">
+ xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
<adm:synopsis>
<adm:user-friendly-name />
define the core schema elements to load.
@@ -260,4 +259,41 @@
</ldap:attribute>
</adm:profile>
</adm:property>
+ <adm:property name="json-validation-policy" advanced="true">
+ <adm:synopsis>
+ Specifies the policy that will be used when validating JSON syntax values.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>strict</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:enumeration>
+ <adm:value name="disabled">
+ <adm:synopsis>
+ JSON syntax values will not be validated and, as a result any
+ sequence of bytes will be acceptable.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="lenient">
+ <adm:synopsis>
+ JSON syntax values must comply with RFC 7159 except: 1) comments are
+ allowed, 2) single quotes may be used instead of double quotes,
+ and 3) unquoted control characters are allowed in strings.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="strict">
+ <adm:synopsis>
+ JSON syntax values must strictly conform to RFC 7159.
+ </adm:synopsis>
+ </adm:value>
+ </adm:enumeration>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-json-validation-policy</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
</adm:managed-object>
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JsonSchemaConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JsonSchemaConfiguration.xml
new file mode 100644
index 0000000..09051eb
--- /dev/null
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JsonSchemaConfiguration.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The contents of this file are subject to the terms of the Common Development and
+ Distribution License (the License). You may not use this file except in compliance with the
+ License.
+
+ You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ specific language governing permission and limitations under the License.
+
+ When distributing Covered Software, include this CDDL Header Notice in each file and include
+ the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ Header, with the fields enclosed by brackets [] replaced by your own identifying
+ information: "Portions copyright [year] [name of copyright owner]".
+
+ Copyright 2016 ForgeRock AS.
+ -->
+<adm:managed-object name="json-schema" plural-name="json-schemas"
+ extends="schema-provider"
+ package="org.forgerock.opendj.server.config"
+ xmlns:adm="http://opendj.forgerock.org/admin"
+ xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
+ <adm:synopsis>
+ The JSON Schema Provider provides the ability to configure customized JSON query
+ matching rules.
+ </adm:synopsis>
+ <adm:description>
+ The core schema provides a default 'jsonQueryMatch' equality matching rule for
+ JSON values which match JSON strings according to the LDAP 'caseIgnoreMatch'
+ semantics (i.e trim white space and ignore case differences), as well as the
+ indexing of all JSON fields.
+
+ This schema provider allows users to create custom JSON matching rules which
+ may use different string matching semantics and, more importantly, may only
+ index a restricted set of JSON fields, thereby consuming less backend resources.
+ </adm:description>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:name>ds-cfg-json-schema</ldap:name>
+ <ldap:superior>ds-cfg-schema-provider</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+ <adm:property-override name="java-class" advanced="true">
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>
+ org.opends.server.schema.JsonSchemaProvider
+ </adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ </adm:property-override>
+ <adm:property name="matching-rule-oid" mandatory="true">
+ <adm:synopsis>
+ The numeric OID of the custom JSON matching rule.
+ </adm:synopsis>
+ <adm:syntax>
+ <adm:string>
+ <adm:pattern>
+ <adm:regex>^([0-9.]+\\d)$</adm:regex>
+ <adm:usage>OID</adm:usage>
+ <adm:synopsis>
+ The OID of the matching rule.
+ </adm:synopsis>
+ </adm:pattern>
+ </adm:string>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-matching-rule-oid</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="matching-rule-name">
+ <adm:synopsis>
+ The name of the custom JSON matching rule.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>The matching rule will not have a name.</adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-matching-rule-name</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="case-sensitive-strings">
+ <adm:synopsis>
+ Indicates whether JSON string comparisons should be case-sensitive.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>false</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-case-sensitive-strings</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="ignore-white-space">
+ <adm:synopsis>
+ Indicates whether JSON string comparisons should ignore white-space.
+ </adm:synopsis>
+ <adm:description>
+ When enabled all leading and trailing white space will be removed and
+ intermediate white space will be reduced to a single character.
+ </adm:description>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>true</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-ignore-white-space</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="indexed-field" multi-valued="true">
+ <adm:synopsis>
+ Specifies which JSON fields should be indexed.
+ </adm:synopsis>
+ <adm:description>
+ A field will be indexed if it matches any of the configured field patterns.
+ </adm:description>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>All JSON fields will be indexed.</adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string>
+ <adm:pattern>
+ <adm:regex>.*</adm:regex>
+ <adm:usage>PATTERN</adm:usage>
+ <adm:synopsis>
+ A JSON pointer which may include wild-cards. A single '*' wild-card matches at most a single path
+ element, whereas a double '**' matches zero or more path elements.
+ </adm:synopsis>
+ </adm:pattern>
+ </adm:string>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-indexed-field</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+</adm:managed-object>
diff --git a/opendj-server-legacy/resource/schema/02-config.ldif b/opendj-server-legacy/resource/schema/02-config.ldif
index 8bdd3b4..4d09cbf 100644
--- a/opendj-server-legacy/resource/schema/02-config.ldif
+++ b/opendj-server-legacy/resource/schema/02-config.ldif
@@ -3962,12 +3962,47 @@
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE
X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.196
+ NAME 'ds-cfg-json-validation-policy'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.197
+ NAME 'ds-cfg-matching-rule-oid'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.198
+ NAME 'ds-cfg-matching-rule-name'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.199
+ NAME 'ds-cfg-case-sensitive-strings'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.200
+ NAME 'ds-cfg-ignore-white-space'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.201
NAME 'ds-cfg-show-subordinate-naming-contexts'
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.202
+ NAME 'ds-cfg-indexed-field'
+ EQUALITY caseExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler'
SUP top
@@ -6038,3 +6073,13 @@
MAY ( ds-cfg-rotation-policy $
ds-cfg-retention-policy )
X-ORIGIN 'OpenDJ Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.53
+ NAME 'ds-cfg-json-schema'
+ SUP ds-cfg-schema-provider
+ STRUCTURAL
+ MUST ds-cfg-matching-rule-oid
+ MAY ( ds-cfg-matching-rule-name $
+ ds-cfg-case-sensitive-strings $
+ ds-cfg-ignore-white-space $
+ ds-cfg-indexed-field )
+ X-ORIGIN 'OpenDJ Directory Server' )
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/schema/CoreSchemaProvider.java b/opendj-server-legacy/src/main/java/org/opends/server/schema/CoreSchemaProvider.java
index f2459b6..7dbc0ba 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/schema/CoreSchemaProvider.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/schema/CoreSchemaProvider.java
@@ -16,6 +16,10 @@
package org.opends.server.schema;
import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.VALIDATION_POLICY;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.ValidationPolicy.DISABLED;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.ValidationPolicy.LENIENT;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.ValidationPolicy.STRICT;
import java.util.List;
@@ -76,6 +80,19 @@
.setOption(ALLOW_NON_STANDARD_TELEPHONE_NUMBERS, !configuration.isStrictFormatTelephoneNumbers())
.setOption(ALLOW_ATTRIBUTE_TYPES_WITH_NO_SUP_OR_SYNTAX, configuration.isAllowAttributeTypesWithNoSupOrSyntax());
+ switch (configuration.getJsonValidationPolicy())
+ {
+ case DISABLED:
+ schemaBuilder.setOption(VALIDATION_POLICY, DISABLED);
+ break;
+ case LENIENT:
+ schemaBuilder.setOption(VALIDATION_POLICY, LENIENT);
+ break;
+ case STRICT:
+ schemaBuilder.setOption(VALIDATION_POLICY, STRICT);
+ break;
+ }
+
for (final String oid : configuration.getDisabledMatchingRule())
{
if (!oid.equals(NONE_ELEMENT))
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/schema/JsonSchemaProvider.java b/opendj-server-legacy/src/main/java/org/opends/server/schema/JsonSchemaProvider.java
new file mode 100644
index 0000000..4d4219d
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/schema/JsonSchemaProvider.java
@@ -0,0 +1,152 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.schema;
+
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.CASE_SENSITIVE_STRINGS;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.IGNORE_WHITE_SPACE;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.INDEXED_FIELD_PATTERNS;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.newJsonQueryEqualityMatchingRuleImpl;
+import static org.forgerock.util.Options.defaultOptions;
+
+import java.util.List;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.config.server.ConfigChangeResult;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.config.server.ConfigurationChangeListener;
+import org.forgerock.opendj.ldap.schema.MatchingRule;
+import org.forgerock.opendj.ldap.schema.MatchingRuleImpl;
+import org.forgerock.opendj.ldap.schema.SchemaBuilder;
+import org.forgerock.opendj.rest2ldap.schema.JsonSchema;
+import org.forgerock.opendj.server.config.server.JsonSchemaCfg;
+import org.forgerock.util.Options;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ServerContext;
+import org.opends.server.schema.SchemaHandler.SchemaUpdater;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.InitializationException;
+
+/** Allows users to configure custom JSON matching rules and indexing. */
+public class JsonSchemaProvider implements SchemaProvider<JsonSchemaCfg>, ConfigurationChangeListener<JsonSchemaCfg>
+{
+ /** The current configuration of JSON schema. */
+ private JsonSchemaCfg currentConfig;
+ private ServerContext serverContext;
+
+ @Override
+ public void initialize(final ServerContext serverContext, final JsonSchemaCfg configuration,
+ final SchemaBuilder initialSchemaBuilder) throws ConfigException, InitializationException
+ {
+ this.serverContext = serverContext;
+ this.currentConfig = configuration;
+
+ addCustomJsonMatchingRule(initialSchemaBuilder, configuration);
+ currentConfig.addJsonSchemaChangeListener(this);
+ }
+
+ private void addCustomJsonMatchingRule(final SchemaBuilder schemaBuilder, final JsonSchemaCfg configuration)
+ {
+ if (!configuration.isEnabled())
+ {
+ return;
+ }
+
+ final String nameOrOid = configuration.getMatchingRuleName() != null
+ ? configuration.getMatchingRuleName() : configuration.getMatchingRuleOid();
+ final Options options = defaultOptions().set(CASE_SENSITIVE_STRINGS, configuration.isCaseSensitiveStrings())
+ .set(IGNORE_WHITE_SPACE, configuration.isIgnoreWhiteSpace())
+ .set(INDEXED_FIELD_PATTERNS, configuration.getIndexedField());
+ final MatchingRuleImpl matchingRuleImpl = newJsonQueryEqualityMatchingRuleImpl(nameOrOid, options);
+ final MatchingRule.Builder builder = schemaBuilder.buildMatchingRule(configuration.getMatchingRuleOid())
+ .syntaxOID(JsonSchema.getJsonQuerySyntax().getOID())
+ .implementation(matchingRuleImpl);
+ if (configuration.getMatchingRuleName() != null)
+ {
+ builder.names(configuration.getMatchingRuleName());
+ }
+ // Let users overwrite core matching rule definitions in order to control indexing.
+ builder.addToSchemaOverwrite();
+ }
+
+ @Override
+ public void finalizeProvider()
+ {
+ if (currentConfig.isEnabled())
+ {
+ try
+ {
+ serverContext.getSchemaHandler().updateSchema(new SchemaUpdater()
+ {
+ @Override
+ public void update(SchemaBuilder builder)
+ {
+ builder.removeMatchingRule(currentConfig.getMatchingRuleOid());
+ }
+ });
+ }
+ catch (DirectoryException e)
+ {
+ // Ignore.
+ }
+ }
+ currentConfig.removeJsonSchemaChangeListener(this);
+ }
+
+ @Override
+ public boolean isConfigurationAcceptable(final JsonSchemaCfg configuration,
+ final List<LocalizableMessage> unacceptableReasons)
+ {
+ return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
+ }
+
+ @Override
+ public boolean isConfigurationChangeAcceptable(final JsonSchemaCfg configuration,
+ final List<LocalizableMessage> unacceptableReasons)
+ {
+ return true;
+ }
+
+ @Override
+ public ConfigChangeResult applyConfigurationChange(final JsonSchemaCfg configuration)
+ {
+ final ConfigChangeResult ccr = new ConfigChangeResult();
+ try
+ {
+ serverContext.getSchemaHandler().updateSchema(new SchemaUpdater()
+ {
+ @Override
+ public void update(SchemaBuilder builder)
+ {
+ if (currentConfig.isEnabled())
+ {
+ builder.removeMatchingRule(currentConfig.getMatchingRuleOid());
+ }
+ addCustomJsonMatchingRule(builder, configuration);
+ }
+ });
+ }
+ catch (DirectoryException e)
+ {
+ ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
+ ccr.addMessage(e.getMessageObject());
+ }
+ finally
+ {
+ currentConfig = configuration;
+ }
+ return ccr;
+ }
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaHandler.java
index 354cf36..378d73f 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaHandler.java
@@ -15,6 +15,7 @@
*/
package org.opends.server.schema;
+import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.addJsonSyntaxesAndMatchingRulesToSchema;
import static org.opends.server.util.SchemaUtils.is02ConfigLdif;
import static java.util.Collections.emptyList;
@@ -209,10 +210,10 @@
// Start from the core schema
final SchemaBuilder schemaBuilder = new SchemaBuilder(Schema.getCoreSchema());
- loadSchemaFromProviders(serverContext.getRootConfig(), schemaBuilder);
-
+ // Load core syntaxes and matching rules first then let providers adjust them if needed.
addServerSyntaxesAndMatchingRules(schemaBuilder);
+ loadSchemaFromProviders(serverContext.getRootConfig(), schemaBuilder);
loadSchemaFromFiles(schemaBuilder);
try
@@ -244,6 +245,7 @@
addHistoricalCsnOrderingMatchingRule(schemaBuilder);
addAuthPasswordEqualityMatchingRule(schemaBuilder);
addUserPasswordEqualityMatchingRule(schemaBuilder);
+ addJsonSyntaxesAndMatchingRulesToSchema(schemaBuilder);
}
catch (ConflictingSchemaElementException e)
{
@@ -705,8 +707,6 @@
* The root to retrieve schema provider configurations.
* @param schemaBuilder
* The schema builder that providers should update.
- * @param schemaUpdater
- * The updater that providers should use when applying a configuration change.
*/
private void loadSchemaFromProviders(final RootCfg rootConfiguration, final SchemaBuilder schemaBuilder)
throws ConfigException, InitializationException {
--
Gitblit v1.10.0