001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2012-2014 ForgeRock AS. 015 */ 016package org.forgerock.opendj.rest2ldap; 017 018import java.util.ArrayList; 019import java.util.List; 020import java.util.Set; 021 022import org.forgerock.json.fluent.JsonPointer; 023import org.forgerock.json.fluent.JsonValue; 024import org.forgerock.json.resource.BadRequestException; 025import org.forgerock.json.resource.ResultHandler; 026import org.forgerock.opendj.ldap.Attribute; 027import org.forgerock.opendj.ldap.AttributeDescription; 028import org.forgerock.opendj.ldap.ByteString; 029import org.forgerock.opendj.ldap.Entry; 030import org.forgerock.opendj.ldap.Filter; 031import org.forgerock.util.Function; 032import org.forgerock.util.promise.NeverThrowsException; 033 034import static java.util.Collections.*; 035 036import static org.forgerock.opendj.ldap.Filter.*; 037import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*; 038import static org.forgerock.opendj.rest2ldap.Utils.*; 039 040/** 041 * An attribute mapper which provides a simple mapping from a JSON value to a 042 * single LDAP attribute. 043 */ 044public final class SimpleAttributeMapper extends AbstractLDAPAttributeMapper<SimpleAttributeMapper> { 045 private Function<ByteString, ?, NeverThrowsException> decoder; 046 private Function<Object, ByteString, NeverThrowsException> encoder; 047 048 SimpleAttributeMapper(final AttributeDescription ldapAttributeName) { 049 super(ldapAttributeName); 050 } 051 052 /** 053 * Sets the decoder which will be used for converting LDAP attribute values 054 * to JSON values. 055 * 056 * @param f 057 * The function to use for decoding LDAP attribute values. 058 * @return This attribute mapper. 059 */ 060 public SimpleAttributeMapper decoder(final Function<ByteString, ?, NeverThrowsException> f) { 061 this.decoder = f; 062 return this; 063 } 064 065 /** 066 * Sets the default JSON value which should be substituted when the LDAP 067 * attribute is not found in the LDAP entry. 068 * 069 * @param defaultValue 070 * The default JSON value. 071 * @return This attribute mapper. 072 */ 073 public SimpleAttributeMapper defaultJSONValue(final Object defaultValue) { 074 this.defaultJSONValues = defaultValue != null ? singletonList(defaultValue) : emptyList(); 075 return this; 076 } 077 078 /** 079 * Sets the encoder which will be used for converting JSON values to LDAP 080 * attribute values. 081 * 082 * @param f 083 * The function to use for encoding LDAP attribute values. 084 * @return This attribute mapper. 085 */ 086 public SimpleAttributeMapper encoder(final Function<Object, ByteString, NeverThrowsException> f) { 087 this.encoder = f; 088 return this; 089 } 090 091 /** 092 * Indicates that JSON values are base 64 encodings of binary data. Calling 093 * this method is equivalent to the following: 094 * 095 * <pre> 096 * mapper.decoder(...); // function that converts binary data to base 64 097 * mapper.encoder(...); // function that converts base 64 to binary data 098 * </pre> 099 * 100 * @return This attribute mapper. 101 */ 102 public SimpleAttributeMapper isBinary() { 103 decoder = byteStringToBase64(); 104 encoder = base64ToByteString(); 105 return this; 106 } 107 108 @Override 109 public String toString() { 110 return "simple(" + ldapAttributeName + ")"; 111 } 112 113 @Override 114 void getLDAPFilter(final Context c, final JsonPointer path, final JsonPointer subPath, 115 final FilterType type, final String operator, final Object valueAssertion, 116 final ResultHandler<Filter> h) { 117 if (subPath.isEmpty()) { 118 try { 119 final ByteString va = 120 valueAssertion != null ? encoder().apply(valueAssertion) : null; 121 h.handleResult(toFilter(c, type, ldapAttributeName.toString(), va)); 122 } catch (final Exception e) { 123 // Invalid assertion value - bad request. 124 h.handleError(new BadRequestException(i18n( 125 "The request cannot be processed because it contained an " 126 + "illegal filter assertion value '%s' for field '%s'", String 127 .valueOf(valueAssertion), path), e)); 128 } 129 } else { 130 // This attribute mapper does not support partial filtering. 131 h.handleResult(alwaysFalse()); 132 } 133 } 134 135 @Override 136 void getNewLDAPAttributes(final Context c, final JsonPointer path, 137 final List<Object> newValues, final ResultHandler<Attribute> h) { 138 try { 139 h.handleResult(jsonToAttribute(newValues, ldapAttributeName, encoder())); 140 } catch (final Exception ex) { 141 h.handleError(new BadRequestException(i18n( 142 "The request cannot be processed because an error occurred while " 143 + "encoding the values for the field '%s': %s", path, ex.getMessage()))); 144 } 145 } 146 147 @Override 148 SimpleAttributeMapper getThis() { 149 return this; 150 } 151 152 @Override 153 void read(final Context c, final JsonPointer path, final Entry e, 154 final ResultHandler<JsonValue> h) { 155 try { 156 final Object value; 157 if (attributeIsSingleValued()) { 158 value = 159 e.parseAttribute(ldapAttributeName).as(decoder(), 160 defaultJSONValues.isEmpty() ? null : defaultJSONValues.get(0)); 161 } else { 162 final Set<Object> s = 163 e.parseAttribute(ldapAttributeName).asSetOf(decoder(), defaultJSONValues); 164 value = s.isEmpty() ? null : new ArrayList<Object>(s); 165 } 166 h.handleResult(value != null ? new JsonValue(value) : null); 167 } catch (final Exception ex) { 168 // The LDAP attribute could not be decoded. 169 h.handleError(asResourceException(ex)); 170 } 171 } 172 173 private Function<ByteString, ? extends Object, NeverThrowsException> decoder() { 174 return decoder == null ? byteStringToJson(ldapAttributeName) : decoder; 175 } 176 177 private Function<Object, ByteString, NeverThrowsException> encoder() { 178 return encoder == null ? jsonToByteString(ldapAttributeName) : encoder; 179 } 180 181}