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-2015 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.JsonPointer; 023import org.forgerock.json.JsonValue; 024import org.forgerock.json.resource.BadRequestException; 025import org.forgerock.json.resource.ResourceException; 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; 033import org.forgerock.util.promise.Promise; 034 035import static java.util.Collections.*; 036 037import static org.forgerock.opendj.ldap.Filter.*; 038import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*; 039import static org.forgerock.opendj.rest2ldap.Utils.*; 040import static org.forgerock.util.promise.Promises.newExceptionPromise; 041import static org.forgerock.util.promise.Promises.newResultPromise; 042 043/** 044 * An attribute mapper which provides a simple mapping from a JSON value to a 045 * single LDAP attribute. 046 */ 047public final class SimpleAttributeMapper extends AbstractLDAPAttributeMapper<SimpleAttributeMapper> { 048 private Function<ByteString, ?, NeverThrowsException> decoder; 049 private Function<Object, ByteString, NeverThrowsException> encoder; 050 051 SimpleAttributeMapper(final AttributeDescription ldapAttributeName) { 052 super(ldapAttributeName); 053 } 054 055 /** 056 * Sets the decoder which will be used for converting LDAP attribute values 057 * to JSON values. 058 * 059 * @param f 060 * The function to use for decoding LDAP attribute values. 061 * @return This attribute mapper. 062 */ 063 public SimpleAttributeMapper decoder(final Function<ByteString, ?, NeverThrowsException> f) { 064 this.decoder = f; 065 return this; 066 } 067 068 /** 069 * Sets the default JSON value which should be substituted when the LDAP 070 * attribute is not found in the LDAP entry. 071 * 072 * @param defaultValue 073 * The default JSON value. 074 * @return This attribute mapper. 075 */ 076 public SimpleAttributeMapper defaultJSONValue(final Object defaultValue) { 077 this.defaultJSONValues = defaultValue != null ? singletonList(defaultValue) : emptyList(); 078 return this; 079 } 080 081 /** 082 * Sets the encoder which will be used for converting JSON values to LDAP 083 * attribute values. 084 * 085 * @param f 086 * The function to use for encoding LDAP attribute values. 087 * @return This attribute mapper. 088 */ 089 public SimpleAttributeMapper encoder(final Function<Object, ByteString, NeverThrowsException> f) { 090 this.encoder = f; 091 return this; 092 } 093 094 /** 095 * Indicates that JSON values are base 64 encodings of binary data. Calling 096 * this method is equivalent to the following: 097 * 098 * <pre> 099 * mapper.decoder(...); // function that converts binary data to base 64 100 * mapper.encoder(...); // function that converts base 64 to binary data 101 * </pre> 102 * 103 * @return This attribute mapper. 104 */ 105 public SimpleAttributeMapper isBinary() { 106 decoder = byteStringToBase64(); 107 encoder = base64ToByteString(); 108 return this; 109 } 110 111 @Override 112 public String toString() { 113 return "simple(" + ldapAttributeName + ")"; 114 } 115 116 @Override 117 Promise<Filter, ResourceException> getLDAPFilter(final RequestState requestState, final JsonPointer path, 118 final JsonPointer subPath, final FilterType type, final String operator, final Object valueAssertion) { 119 if (subPath.isEmpty()) { 120 try { 121 final ByteString va = 122 valueAssertion != null ? encoder().apply(valueAssertion) : null; 123 return newResultPromise(toFilter(type, ldapAttributeName.toString(), va)); 124 } catch (final Exception e) { 125 // Invalid assertion value - bad request. 126 return newExceptionPromise((ResourceException) new BadRequestException(i18n( 127 "The request cannot be processed because it contained an " 128 + "illegal filter assertion value '%s' for field '%s'", 129 String.valueOf(valueAssertion), path), e)); 130 } 131 } else { 132 // This attribute mapper does not support partial filtering. 133 return newResultPromise(alwaysFalse()); 134 } 135 } 136 137 @Override 138 Promise<Attribute, ResourceException> getNewLDAPAttributes( 139 final RequestState requestState, final JsonPointer path, final List<Object> newValues) { 140 try { 141 return newResultPromise(jsonToAttribute(newValues, ldapAttributeName, encoder())); 142 } catch (final Exception ex) { 143 return newExceptionPromise((ResourceException) new BadRequestException(i18n( 144 "The request cannot be processed because an error occurred while " 145 + "encoding the values for the field '%s': %s", path, ex.getMessage()))); 146 } 147 } 148 149 @Override 150 SimpleAttributeMapper getThis() { 151 return this; 152 } 153 154 @Override 155 Promise<JsonValue, ResourceException> read(final RequestState requestState, final JsonPointer path, final Entry e) { 156 try { 157 final Object value; 158 if (attributeIsSingleValued()) { 159 value = 160 e.parseAttribute(ldapAttributeName).as(decoder(), 161 defaultJSONValues.isEmpty() ? null : defaultJSONValues.get(0)); 162 } else { 163 final Set<Object> s = 164 e.parseAttribute(ldapAttributeName).asSetOf(decoder(), defaultJSONValues); 165 value = s.isEmpty() ? null : new ArrayList<>(s); 166 } 167 return newResultPromise(value != null ? new JsonValue(value) : null); 168 } catch (final Exception ex) { 169 // The LDAP attribute could not be decoded. 170 return newExceptionPromise(asResourceException(ex)); 171 } 172 } 173 174 private Function<ByteString, ?, NeverThrowsException> decoder() { 175 return decoder == null ? byteStringToJson(ldapAttributeName) : decoder; 176 } 177 178 private Function<Object, ByteString, NeverThrowsException> encoder() { 179 return encoder == null ? jsonToByteString(ldapAttributeName) : encoder; 180 } 181 182}