opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfigurator.java
@@ -79,6 +79,7 @@ import org.forgerock.opendj.ldap.SSLContextBuilder; import org.forgerock.opendj.ldap.requests.BindRequest; import org.forgerock.opendj.ldap.requests.Requests; import org.forgerock.opendj.rest2ldap.schema.JsonSchema; import org.forgerock.services.context.Context; import org.forgerock.util.Options; import org.forgerock.util.promise.Promise; @@ -356,6 +357,15 @@ .isRequired(mapper.get("isRequired").defaultTo(false).asBoolean()) .isMultiValued(mapper.get("isMultiValued").defaultTo(false).asBoolean()) .writability(parseWritability(mapper)); case "json": return simple(mapper.get("ldapAttribute").defaultTo(defaultLdapAttribute).required().asString()) .defaultJsonValue(mapper.get("defaultJsonValue").getObject()) .isRequired(mapper.get("isRequired").defaultTo(false).asBoolean()) .isMultiValued(mapper.get("isMultiValued").defaultTo(false).asBoolean()) .encoder(JsonSchema.jsonToByteString()) .decoder(JsonSchema.byteStringToJson()) .jsonSchema(mapper.isDefined("schema") ? mapper.get("schema") : null) .writability(parseWritability(mapper)); case "reference": final String ldapAttribute = mapper.get("ldapAttribute") .defaultTo(defaultLdapAttribute).required().asString(); opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -26,6 +26,7 @@ import static org.forgerock.opendj.ldap.schema.CoreSchema.getGeneralizedTimeSyntax; import static org.forgerock.opendj.ldap.schema.CoreSchema.getIntegerSyntax; import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_UNRECOGNIZED_JSON_VALUE; import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.getJsonSyntax; import java.util.Collection; import java.util.Collections; @@ -45,6 +46,7 @@ import org.forgerock.opendj.ldap.GeneralizedTime; import org.forgerock.opendj.ldap.LinkedAttribute; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.opendj.rest2ldap.schema.JsonSchema; import org.forgerock.services.context.Context; import org.forgerock.util.Function; import org.forgerock.util.promise.NeverThrowsException; @@ -54,8 +56,8 @@ */ final class Utils { private static final Function<Object, ByteString, NeverThrowsException> BASE64_TO_BYTESTRING = new Function<Object, ByteString, NeverThrowsException>() { private static final Function<Object, ByteString, LocalizedIllegalArgumentException> BASE64_TO_BYTESTRING = new Function<Object, ByteString, LocalizedIllegalArgumentException>() { @Override public ByteString apply(final Object value) { return ByteString.valueOfBase64(String.valueOf(value)); @@ -70,7 +72,7 @@ } }; static Function<Object, ByteString, NeverThrowsException> base64ToByteString() { static Function<Object, ByteString, LocalizedIllegalArgumentException> base64ToByteString() { return BASE64_TO_BYTESTRING; } @@ -78,8 +80,9 @@ return BYTESTRING_TO_BASE64; } static Function<ByteString, Object, NeverThrowsException> byteStringToJson(final AttributeDescription ad) { return new Function<ByteString, Object, NeverThrowsException>() { static Function<ByteString, Object, LocalizedIllegalArgumentException> byteStringToJson( final AttributeDescription ad) { return new Function<ByteString, Object, LocalizedIllegalArgumentException>() { @Override public Object apply(final ByteString value) { final Syntax syntax = ad.getAttributeType().getSyntax(); @@ -88,8 +91,9 @@ } else if (syntax.equals(getIntegerSyntax())) { return byteStringToLong().apply(value); } else if (syntax.equals(getGeneralizedTimeSyntax())) { return printDateTime(byteStringToGeneralizedTime().apply(value) .toCalendar()); return printDateTime(byteStringToGeneralizedTime().apply(value).toCalendar()); } else if (syntax.equals(getJsonSyntax())) { return JsonSchema.byteStringToJson().apply(value); } else { return byteStringToString().apply(value); } @@ -120,17 +124,20 @@ } } static Function<Object, ByteString, NeverThrowsException> jsonToByteString(final AttributeDescription ad) { return new Function<Object, ByteString, NeverThrowsException>() { static Function<Object, ByteString, Exception> jsonToByteString( final AttributeDescription ad) { return new Function<Object, ByteString, Exception>() { @Override public ByteString apply(final Object value) { if (isJsonPrimitive(value)) { public ByteString apply(final Object value) throws Exception { final Syntax syntax = ad.getAttributeType().getSyntax(); if (isJsonPrimitive(value)) { if (syntax.equals(getGeneralizedTimeSyntax())) { return ByteString.valueOfObject(GeneralizedTime.valueOf(parseDateTime(value.toString()))); } else { return ByteString.valueOfObject(value); } } else if (syntax.equals(getJsonSyntax())) { return JsonSchema.jsonToByteString().apply(value); } else { throw new LocalizedIllegalArgumentException(ERR_UNRECOGNIZED_JSON_VALUE.get(value.getClass() .getName())); opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/schema/JsonQueryEqualityMatchingRuleImpl.java
@@ -25,6 +25,7 @@ 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.ValidationPolicy.LENIENT; import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.jsonParsingException; import java.io.IOException; import java.io.InputStream; @@ -56,7 +57,6 @@ import org.forgerock.util.query.QueryFilterVisitor; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; /** @@ -203,12 +203,8 @@ return normalizedValue.toByteString(); } catch (DecodeException e) { throw e; } catch (JsonProcessingException e) { throw DecodeException.error(ERR_JSON_PARSE_ERROR.get(e.getLocation().getLineNr(), e.getLocation().getColumnNr(), e.getOriginalMessage())); } catch (IOException e) { throw DecodeException.error(ERR_JSON_IO_ERROR.get(e.getMessage())); throw DecodeException.error(jsonParsingException(e)); } } @@ -380,12 +376,8 @@ } } catch (DecodeException e) { throw e; } catch (JsonProcessingException e) { throw DecodeException.error(ERR_JSON_PARSE_ERROR.get(e.getLocation().getLineNr(), e.getLocation().getColumnNr(), e.getOriginalMessage())); } catch (IOException e) { throw DecodeException.error(ERR_JSON_IO_ERROR.get(e.getMessage())); throw DecodeException.error(jsonParsingException(e)); } } opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/schema/JsonSchema.java
@@ -20,20 +20,30 @@ import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS; import static java.util.Collections.emptyList; import static org.forgerock.opendj.ldap.schema.Schema.getCoreSchema; import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_JSON_IO_ERROR; import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_JSON_PARSE_ERROR; import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.ValidationPolicy.LENIENT; import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.ValidationPolicy.STRICT; import static org.forgerock.util.Options.defaultOptions; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizedIllegalArgumentException; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleImpl; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldap.schema.SchemaBuilder; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.util.Function; import org.forgerock.util.Option; import org.forgerock.util.Options; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; /** @@ -54,6 +64,7 @@ .enable(ALLOW_UNQUOTED_CONTROL_CHARS)), /** JSON validation policy which does not perform any validation. */ DISABLED(null); private final ObjectMapper objectMapper; ValidationPolicy(final ObjectMapper objectMapper) { @@ -128,6 +139,37 @@ private static final Syntax JSON_QUERY_SYNTAX; private static final MatchingRule CASE_IGNORE_JSON_QUERY_MATCHING_RULE; private static final MatchingRule CASE_EXACT_JSON_QUERY_MATCHING_RULE; private static final Function<ByteString, Object, LocalizedIllegalArgumentException> BYTESTRING_TO_JSON = new Function<ByteString, Object, LocalizedIllegalArgumentException>() { @Override public Object apply(final ByteString value) { try (final InputStream inputStream = value.asReader().asInputStream()) { return LENIENT.getObjectMapper().readValue(inputStream, Object.class); } catch (final IOException e) { throw new LocalizedIllegalArgumentException(jsonParsingException(e)); } } }; static LocalizableMessage jsonParsingException(final IOException e) { if (e instanceof JsonProcessingException) { final JsonProcessingException jpe = (JsonProcessingException) e; if (jpe.getLocation() != null) { return ERR_JSON_PARSE_ERROR.get(jpe.getLocation().getLineNr(), jpe.getLocation().getColumnNr(), jpe.getOriginalMessage()); } } return ERR_JSON_IO_ERROR.get(e.getMessage()); } private static final Function<Object, ByteString, JsonProcessingException> JSON_TO_BYTESTRING = new Function<Object, ByteString, JsonProcessingException>() { @Override public ByteString apply(final Object value) throws JsonProcessingException { return ByteString.wrap(LENIENT.getObjectMapper().writeValueAsBytes(value)); } }; static { final Schema schema = addJsonSyntaxesAndMatchingRulesToSchema(new SchemaBuilder(getCoreSchema())).toSchema(); @@ -247,6 +289,25 @@ return builder; } /** * Returns a function which parses {@code JSON} values. Invalid values will result in a * {@code LocalizedIllegalArgumentException}. * * @return A function which parses {@code JSON} values. */ public static Function<ByteString, Object, LocalizedIllegalArgumentException> byteStringToJson() { return BYTESTRING_TO_JSON; } /** * Returns a function which converts a JSON {@code Object} to a {@code ByteString}. * * @return A function which converts a JSON {@code Object} to a {@code ByteString}. */ public static Function<Object, ByteString, JsonProcessingException> jsonToByteString() { return JSON_TO_BYTESTRING; } private JsonSchema() { // Prevent instantiation. } opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/schema/JsonSyntaxImpl.java
@@ -16,13 +16,12 @@ package org.forgerock.opendj.rest2ldap.schema; import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_JSON_EMPTY_CONTENT; import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_JSON_IO_ERROR; import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_JSON_PARSE_ERROR; import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.ERR_JSON_TRAILING_CONTENT; import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.EMR_CASE_IGNORE_JSON_QUERY_OID; 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.SYNTAX_JSON_DESCRIPTION; import static org.forgerock.opendj.rest2ldap.schema.JsonSchema.jsonParsingException; import java.io.IOException; import java.io.InputStream; @@ -34,7 +33,6 @@ import org.forgerock.opendj.rest2ldap.schema.JsonSchema.ValidationPolicy; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; /** This class implements the JSON attribute syntax. */ @@ -110,13 +108,8 @@ return false; } return true; } catch (JsonProcessingException e) { invalidReason.append(ERR_JSON_PARSE_ERROR.get(e.getLocation().getLineNr(), e.getLocation().getColumnNr(), e.getOriginalMessage())); return false; } catch (IOException e) { invalidReason.append(ERR_JSON_IO_ERROR.get(e.getMessage())); invalidReason.append(jsonParsingException(e)); return false; } }