| | |
| | | * An attribute mapper is responsible for converting JSON values to and from |
| | | * LDAP attributes. |
| | | */ |
| | | public interface AttributeMapper { |
| | | public abstract class AttributeMapper { |
| | | /* |
| | | * This interface is an abstract class so that methods can be made package |
| | | * private until API is finalized. |
| | | */ |
| | | |
| | | AttributeMapper() { |
| | | // Nothing to do. |
| | | } |
| | | |
| | | /** |
| | | * Adds the names of the LDAP attributes required by this attribute mapper |
| | |
| | | * The set into which the required LDAP attribute names should be |
| | | * put. |
| | | */ |
| | | void getLDAPAttributes(Context c, JsonPointer jsonAttribute, Set<String> ldapAttributes); |
| | | abstract void getLDAPAttributes(Context c, JsonPointer jsonAttribute, Set<String> ldapAttributes); |
| | | |
| | | /** |
| | | * Transforms the provided REST comparison filter parameters to an LDAP |
| | |
| | | * @param h |
| | | * The result handler. |
| | | */ |
| | | void getLDAPFilter(Context c, FilterType type, JsonPointer jsonAttribute, String operator, |
| | | Object valueAssertion, ResultHandler<Filter> h); |
| | | abstract void getLDAPFilter(Context c, FilterType type, JsonPointer jsonAttribute, |
| | | String operator, Object valueAssertion, ResultHandler<Filter> h); |
| | | |
| | | /** |
| | | * Transforms attributes contained in the provided LDAP entry to JSON |
| | |
| | | * @param h |
| | | * The result handler. |
| | | */ |
| | | void toJSON(Context c, Entry e, ResultHandler<Map<String, Object>> h); |
| | | abstract void toJSON(Context c, Entry e, ResultHandler<Map<String, Object>> h); |
| | | |
| | | /** |
| | | * Transforms JSON content in the provided JSON value to LDAP attributes, |
| | |
| | | * @param h |
| | | * The result handler. |
| | | */ |
| | | void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h); |
| | | abstract void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h); |
| | | |
| | | // TODO: methods for obtaining schema information (e.g. name, description, |
| | | // type information). |
| | |
| | | * An attribute mapper which maps a single JSON attribute to the result of |
| | | * another attribute mapper. |
| | | */ |
| | | public class ComplexAttributeMapper implements AttributeMapper { |
| | | final class ComplexAttributeMapper extends AttributeMapper { |
| | | |
| | | private final String jsonAttributeName; |
| | | private final AttributeMapper mapper; |
| | |
| | | * The mapper which should be used to provide the contents of the |
| | | * complex attribute. |
| | | */ |
| | | public ComplexAttributeMapper(final String jsonAttributeName, final AttributeMapper mapper) { |
| | | ComplexAttributeMapper(final String jsonAttributeName, final AttributeMapper mapper) { |
| | | this.jsonAttributeName = jsonAttributeName; |
| | | this.mapper = mapper; |
| | | this.normalizedJsonAttributeName = toLowerCase(jsonAttributeName); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | if (jsonAttribute.isEmpty() || matches(jsonAttribute)) { |
| | | final JsonPointer relativePointer = jsonAttribute.relativePointer(); |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute, |
| | | final String operator, final Object valueAssertion, final ResultHandler<Filter> h) { |
| | | if (matches(jsonAttribute)) { |
| | | final JsonPointer relativePointer = jsonAttribute.relativePointer(); |
| | | mapper.getLDAPFilter(c, type, relativePointer, operator, valueAssertion, h); |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, |
| | | final ResultHandler<Map<String, Object>> h) { |
| | | void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | final ResultHandler<Map<String, Object>> wrapper = |
| | | new ResultHandler<Map<String, Object>>() { |
| | | |
| | |
| | | mapper.toJSON(c, e, wrapper); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final Context c, final JsonValue v, |
| | | final ResultHandler<List<Attribute>> h) { |
| | | void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | } |
| | | |
| | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.transform; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | |
| | | * An attribute mapper which combines the results of a set of subordinate |
| | | * attribute mappers into a single JSON object. |
| | | */ |
| | | public final class CompositeAttributeMapper implements AttributeMapper { |
| | | private final List<AttributeMapper> attributeMappers = new LinkedList<AttributeMapper>(); |
| | | final class CompositeAttributeMapper extends AttributeMapper { |
| | | private final List<AttributeMapper> attributeMappers; |
| | | |
| | | /** |
| | | * Creates a new composite attribute mapper. |
| | | */ |
| | | public CompositeAttributeMapper() { |
| | | // No implementation required. |
| | | CompositeAttributeMapper(final Collection<AttributeMapper> mappers) { |
| | | this.attributeMappers = new ArrayList<AttributeMapper>(mappers); |
| | | } |
| | | |
| | | /** |
| | | * Adds a subordinate attribute mapper to this composite attribute mapper. |
| | | * |
| | | * @param mapper |
| | | * The subordinate attribute mapper. |
| | | * @return This composite attribute mapper. |
| | | */ |
| | | public CompositeAttributeMapper addMapper(final AttributeMapper mapper) { |
| | | attributeMappers.add(mapper); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | for (final AttributeMapper attribute : attributeMappers) { |
| | | attribute.getLDAPAttributes(c, jsonAttribute, ldapAttributes); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute, |
| | | final String operator, final Object valueAssertion, final ResultHandler<Filter> h) { |
| | | final ResultHandler<Filter> handler = |
| | | accumulate(attributeMappers.size(), transform( |
| | | new Function<List<Filter>, Filter, Void>() { |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | final ResultHandler<Map<String, Object>> handler = |
| | | accumulate(attributeMappers.size(), transform( |
| | | new Function<List<Map<String, Object>>, Map<String, Object>, Void>() { |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | } |
| | | |
| | |
| | | /** |
| | | * An attribute mapper which maps a single JSON attribute to a fixed value. |
| | | */ |
| | | public class ConstantAttributeMapper implements AttributeMapper { |
| | | final class ConstantAttributeMapper extends AttributeMapper { |
| | | private final String jsonAttributeName; |
| | | private final Object jsonAttributeValue; |
| | | |
| | |
| | | * @param attributeValue |
| | | * The value of the simple JSON attribute. |
| | | */ |
| | | public ConstantAttributeMapper(final String attributeName, final Object attributeValue) { |
| | | ConstantAttributeMapper(final String attributeName, final Object attributeValue) { |
| | | this.jsonAttributeName = attributeName; |
| | | this.jsonAttributeValue = attributeValue; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | // Nothing to do. |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute, |
| | | final String operator, final Object valueAssertion, final ResultHandler<Filter> h) { |
| | | if (jsonAttribute.size() == 1 && jsonAttribute.get(0).equalsIgnoreCase(jsonAttributeName)) { |
| | | final Filter filter; |
| | | if (type == FilterType.PRESENT) { |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | // FIXME: how do we know if the user requested it??? |
| | | h.handleResult(Collections.singletonMap(jsonAttributeName, jsonAttributeValue)); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | } |
| | | |
| | | private <T extends Comparable<T>> Filter compare(Context c, final FilterType type, final T v1, |
| | | final T v2) { |
| | | private <T extends Comparable<T>> Filter compare(final Context c, final FilterType type, |
| | | final T v1, final T v2) { |
| | | final Filter filter; |
| | | switch (type) { |
| | | case EQUAL_TO: |
| | |
| | | break; |
| | | case GREATER_THAN: |
| | | filter = |
| | | v1.compareTo(v2) > 0 ? c.getConfig().trueFilter() : c.getConfig() |
| | | .falseFilter(); |
| | | v1.compareTo(v2) > 0 ? c.getConfig().trueFilter() : c.getConfig().falseFilter(); |
| | | break; |
| | | case GREATER_THAN_OR_EQUAL_TO: |
| | | filter = |
| | |
| | | break; |
| | | case LESS_THAN: |
| | | filter = |
| | | v1.compareTo(v2) < 0 ? c.getConfig().trueFilter() : c.getConfig() |
| | | .falseFilter(); |
| | | v1.compareTo(v2) < 0 ? c.getConfig().trueFilter() : c.getConfig().falseFilter(); |
| | | break; |
| | | case LESS_THAN_OR_EQUAL_TO: |
| | | filter = |
| | |
| | | private final Config config; |
| | | private final ServerContext context; |
| | | |
| | | Context(Config config, ServerContext context) { |
| | | Context(final Config config, final ServerContext context) { |
| | | this.config = config; |
| | | this.context = context; |
| | | } |
| | |
| | | * An attribute mapper that directly maps a configurable selection of attributes |
| | | * to and from LDAP without any transformation. |
| | | */ |
| | | public final class DefaultAttributeMapper implements AttributeMapper { |
| | | final class DefaultAttributeMapper extends AttributeMapper { |
| | | // All user attributes by default. |
| | | private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>(); |
| | | private final Map<String, String> includedAttributes = new LinkedHashMap<String, String>(); |
| | |
| | | * Creates a new default attribute mapper which will map all user attributes |
| | | * to JSON by default. |
| | | */ |
| | | public DefaultAttributeMapper() { |
| | | DefaultAttributeMapper() { |
| | | // No implementation required. |
| | | } |
| | | |
| | |
| | | * The attributes to be excluded. |
| | | * @return This attribute mapper. |
| | | */ |
| | | public DefaultAttributeMapper excludeAttribute(final String... attributes) { |
| | | DefaultAttributeMapper excludeAttribute(final String... attributes) { |
| | | for (final String attribute : attributes) { |
| | | excludedAttributes.put(toLowerCase(attribute), attribute); |
| | | } |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | switch (jsonAttribute.size()) { |
| | | case 0: |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute, |
| | | final String operator, final Object valueAssertion, final ResultHandler<Filter> h) { |
| | | if (jsonAttribute.size() == 1 && isIncludedAttribute(jsonAttribute.get(0))) { |
| | | h.handleResult(toFilter(c, type, jsonAttribute.get(0), valueAssertion)); |
| | | } else { |
| | |
| | | * The attributes to be included. |
| | | * @return This attribute mapper. |
| | | */ |
| | | public DefaultAttributeMapper includeAttribute(final String... attributes) { |
| | | DefaultAttributeMapper includeAttribute(final String... attributes) { |
| | | for (final String attribute : attributes) { |
| | | includedAttributes.put(toLowerCase(attribute), attribute); |
| | | } |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | // FIXME: this will leave out attributes which were not included in the LDAP entry. We should |
| | | // ensure that JSON attributes are present, even if they are null or an empty array. |
| | | final Map<String, Object> result = new LinkedHashMap<String, Object>(e.getAttributeCount()); |
| | | for (final Attribute a : e.getAllAttributes()) { |
| | | final String name = getAttributeName(a); |
| | |
| | | h.handleResult(result); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO: |
| | | } |
| | | |
| | |
| | | import org.forgerock.json.resource.UncategorizedException; |
| | | import org.forgerock.json.resource.UpdateRequest; |
| | | import org.forgerock.opendj.ldap.AssertionFailureException; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AuthenticationException; |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ConnectionException; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.TimeoutResultException; |
| | | import org.forgerock.opendj.ldap.requests.AddRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | |
| | | * A {@code CollectionResourceProvider} implementation which maps a JSON |
| | | * resource collection to LDAP entries beneath a base DN. |
| | | */ |
| | | public class LDAPCollectionResourceProvider implements CollectionResourceProvider { |
| | | final class LDAPCollectionResourceProvider implements CollectionResourceProvider { |
| | | |
| | | private abstract class AbstractRequestCompletionHandler<R, |
| | | H extends org.forgerock.opendj.ldap.ResultHandler<? super R>> |
| | |
| | | super(connection, resultHandler); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public final boolean handleEntry(final SearchResultEntry entry) { |
| | | return resultHandler.handleEntry(entry); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public final boolean handleReference(final SearchResultReference reference) { |
| | | return resultHandler.handleReference(reference); |
| | |
| | | |
| | | } |
| | | |
| | | // FIXME: make this configurable. |
| | | private static final String ETAG_ATTRIBUTE = "etag"; |
| | | |
| | | // Dummy exception used for signalling search success. |
| | | private static final ResourceException SUCCESS = new UncategorizedException(0, null, null); |
| | | |
| | | // FIXME: make this configurable, also allow use of DN. |
| | | private static final String UUID_ATTRIBUTE = "entryUUID"; |
| | | |
| | | private final AttributeMapper attributeMapper; |
| | | private final DN baseDN; // TODO: support template variables. |
| | | private final Config config; |
| | | private final ConnectionFactory factory; |
| | | private final MVCCStrategy mvccStrategy; |
| | | private final NameStrategy nameStrategy; |
| | | |
| | | /** |
| | | * Creates a new LDAP resource. |
| | | * |
| | | * @param baseDN |
| | | * The parent of all entries contained in this LDAP collection. |
| | | * @param mapper |
| | | * The attribute mapper which will be used for mapping LDAP |
| | | * attributes to JSON attributes. |
| | | * @param factory |
| | | * The LDAP connection factory which will be used for performing |
| | | * LDAP operations. |
| | | * @param config |
| | | * Common configuration options. |
| | | */ |
| | | public LDAPCollectionResourceProvider(final DN baseDN, final AttributeMapper mapper, |
| | | final ConnectionFactory factory, final Config config) { |
| | | LDAPCollectionResourceProvider(final DN baseDN, final AttributeMapper mapper, |
| | | final ConnectionFactory factory, final Config config, final NameStrategy nameStrategy, |
| | | final MVCCStrategy mvccStrategy) { |
| | | this.baseDN = baseDN; |
| | | this.attributeMapper = mapper; |
| | | this.factory = factory; |
| | | this.config = config; |
| | | this.nameStrategy = nameStrategy; |
| | | this.mvccStrategy = mvccStrategy; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void actionCollection(final ServerContext context, final ActionRequest request, |
| | | final ResultHandler<JsonValue> handler) { |
| | | handler.handleError(new NotSupportedException("Not yet implemented")); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void actionInstance(final ServerContext context, final String resourceId, |
| | | final ActionRequest request, final ResultHandler<JsonValue> handler) { |
| | | handler.handleError(new NotSupportedException("Not yet implemented")); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void createInstance(final ServerContext context, final CreateRequest request, |
| | | final ResultHandler<Resource> handler) { |
| | | handler.handleError(new NotSupportedException("Not yet implemented")); |
| | | // We will support three use-cases: |
| | | // |
| | | // 1) client provided: the RDN is derived from the ID |
| | | // 2) client provided: the RDN is derived from a JSON attribute, the ID maps to a user attribute |
| | | // 3) server provided: the RDN is derived from a JSON attribute |
| | | // |
| | | // Procedure: |
| | | // |
| | | // 1) Generate LDAP attributes and create entry |
| | | // 2) Apply ID mapper: create RDN from entry/ID, store ID in entry |
| | | // 3) Create add request |
| | | // 4) Add post read control if policy rfc |
| | | // 5) Do add request |
| | | // 6) If add failed then return error |
| | | // 7) If policy is rfc then return entry |
| | | // 8) If policy is search then read entry |
| | | // |
| | | final Context c = wrap(context); |
| | | final AddRequest addRequest = Requests.newAddRequest(DN.rootDN()); |
| | | attributeMapper.toLDAP(c, request.getContent(), new ResultHandler<List<Attribute>>() { |
| | | @Override |
| | | public void handleError(final ResourceException error) { |
| | | handler.handleError(error); |
| | | } |
| | | |
| | | @Override |
| | | public void handleResult(final List<Attribute> result) { |
| | | for (final Attribute attribute : result) { |
| | | addRequest.addAttribute(attribute); |
| | | } |
| | | nameStrategy.setResourceId(c, baseDN, request.getNewResourceId(), addRequest); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void deleteInstance(final ServerContext context, final String resourceId, |
| | | final DeleteRequest request, final ResultHandler<Resource> handler) { |
| | | handler.handleError(new NotSupportedException("Not yet implemented")); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void patchInstance(final ServerContext context, final String resourceId, |
| | | final PatchRequest request, final ResultHandler<Resource> handler) { |
| | | handler.handleError(new NotSupportedException("Not yet implemented")); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void queryCollection(final ServerContext context, final QueryRequest request, |
| | | final QueryResultHandler handler) { |
| | | final Context c = wrap(context); |
| | | final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters()); |
| | | |
| | | // The handler which will be invoked for each LDAP search result. |
| | | final SearchResultHandler searchHandler = new SearchResultHandler() { |
| | |
| | | return false; |
| | | } |
| | | |
| | | // TODO: should the resource or the container define the ID |
| | | // mapping? |
| | | final String id = getIDFromEntry(entry); |
| | | final String revision = getEtagFromEntry(entry); |
| | | final String id = nameStrategy.getResourceId(c, entry); |
| | | final String revision = mvccStrategy.getRevisionFromEntry(c, entry); |
| | | final ResultHandler<Map<String, Object>> mapHandler = |
| | | new ResultHandler<Map<String, Object>>() { |
| | | @Override |
| | |
| | | if (ldapFilter == null || ldapFilter == c.getConfig().falseFilter()) { |
| | | handler.handleResult(new QueryResult()); |
| | | } else { |
| | | final String[] tmp = getSearchAttributes(ldapAttributes); |
| | | final ConnectionCompletionHandler<Result> outerHandler = |
| | | new ConnectionCompletionHandler<Result>(searchHandler) { |
| | | |
| | |
| | | final SearchRequestCompletionHandler innerHandler = |
| | | new SearchRequestCompletionHandler(connection, |
| | | searchHandler); |
| | | final String[] attributes = |
| | | getLDAPAttributes(c, request.getFieldFilters()); |
| | | final SearchRequest request = |
| | | Requests.newSearchRequest(baseDN, |
| | | SearchScope.SINGLE_LEVEL, ldapFilter, tmp); |
| | | Requests.newSearchRequest(getBaseDN(c), |
| | | SearchScope.SINGLE_LEVEL, ldapFilter, |
| | | attributes); |
| | | connection.searchAsync(request, null, innerHandler); |
| | | } |
| | | |
| | |
| | | getLDAPFilter(c, request.getQueryFilter(), filterHandler); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void readInstance(final ServerContext context, final String resourceId, |
| | | final ReadRequest request, final ResultHandler<Resource> handler) { |
| | | final Context c = wrap(context); |
| | | final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters()); |
| | | final String[] tmp = getSearchAttributes(ldapAttributes); |
| | | |
| | | // The handler which will be invoked for the LDAP search result. |
| | | final org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> searchHandler = |
| | |
| | | |
| | | @Override |
| | | public void handleResult(final SearchResultEntry entry) { |
| | | final String revision = getEtagFromEntry(entry); |
| | | final String revision = mvccStrategy.getRevisionFromEntry(c, entry); |
| | | final ResultHandler<Map<String, Object>> mapHandler = |
| | | new ResultHandler<Map<String, Object>>() { |
| | | @Override |
| | |
| | | final RequestCompletionHandler<SearchResultEntry> innerHandler = |
| | | new RequestCompletionHandler<SearchResultEntry>(connection, |
| | | searchHandler); |
| | | final String[] attributes = getLDAPAttributes(c, request.getFieldFilters()); |
| | | final SearchRequest request = |
| | | Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter |
| | | .equality(UUID_ATTRIBUTE, resourceId), tmp); |
| | | nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId) |
| | | .addAttribute(attributes); |
| | | connection.searchSingleEntryAsync(request, innerHandler); |
| | | } |
| | | |
| | |
| | | factory.getConnectionAsync(outerHandler); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void updateInstance(final ServerContext context, final String resourceId, |
| | | final UpdateRequest request, final ResultHandler<Resource> handler) { |
| | |
| | | return ResourceException.getException(resourceResultCode, null, error.getMessage(), error); |
| | | } |
| | | |
| | | /** |
| | | * Returns the ETag for the provided entry. |
| | | * |
| | | * @param entry |
| | | * The entry. |
| | | * @return The ETag. |
| | | */ |
| | | private String getEtagFromEntry(final Entry entry) { |
| | | return entry.parseAttribute(ETAG_ATTRIBUTE).asString(); |
| | | } |
| | | |
| | | /** |
| | | * Returns the resource ID for the provided entry. |
| | | * |
| | | * @param entry |
| | | * The entry. |
| | | * @return The resource ID. |
| | | */ |
| | | private String getIDFromEntry(final Entry entry) { |
| | | return entry.parseAttribute(UUID_ATTRIBUTE).asString(); |
| | | private DN getBaseDN(final Context context) { |
| | | return baseDN; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return The set of LDAP attributes associated with the resource |
| | | * attributes. |
| | | */ |
| | | private Collection<String> getLDAPAttributes(final Context c, |
| | | private String[] getLDAPAttributes(final Context c, |
| | | final Collection<JsonPointer> requestedAttributes) { |
| | | // Get all the LDAP attributes required by the attribute mappers. |
| | | final Set<String> requestedLDAPAttributes; |
| | | if (requestedAttributes.isEmpty()) { |
| | | // Full read. |
| | |
| | | attributeMapper.getLDAPAttributes(c, requestedAttribute, requestedLDAPAttributes); |
| | | } |
| | | } |
| | | return requestedLDAPAttributes; |
| | | |
| | | // Get the LDAP attributes required by the Etag and name stategies. |
| | | nameStrategy.getLDAPAttributes(c, requestedLDAPAttributes); |
| | | mvccStrategy.getLDAPAttributes(c, requestedLDAPAttributes); |
| | | return requestedLDAPAttributes.toArray(new String[requestedLDAPAttributes.size()]); |
| | | } |
| | | |
| | | private void getLDAPFilter(final Context c, final QueryFilter queryFilter, |
| | |
| | | queryFilter.accept(visitor, h); |
| | | } |
| | | |
| | | private String[] getSearchAttributes(final Collection<String> attributes) { |
| | | // FIXME: who is responsible for adding the UUID and etag attributes to |
| | | // this search? |
| | | final String[] tmp = attributes.toArray(new String[attributes.size() + 2]); |
| | | tmp[tmp.length - 2] = UUID_ATTRIBUTE; |
| | | tmp[tmp.length - 1] = ETAG_ATTRIBUTE; |
| | | return tmp; |
| | | } |
| | | |
| | | private Context wrap(final ServerContext context) { |
| | | return new Context(config, context); |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | |
| | | /** |
| | | * A multi-version concurrency control strategy is responsible for ensuring that |
| | | * clients can perform atomic updates to LDAP resources. |
| | | */ |
| | | public abstract class MVCCStrategy { |
| | | /* |
| | | * This interface is an abstract class so that methods can be made package |
| | | * private until API is finalized. |
| | | */ |
| | | |
| | | MVCCStrategy() { |
| | | // Nothing to do. |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the revision value (etag) from the provided LDAP entry. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param entry |
| | | * The LDAP entry. |
| | | * @return The revision value. |
| | | */ |
| | | abstract String getRevisionFromEntry(Context c, Entry entry); |
| | | |
| | | /** |
| | | * Adds the name of any LDAP attribute required by this MVCC strategy to the |
| | | * provided set. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param ldapAttributes |
| | | * The set into which any required LDAP attribute name should be |
| | | * put. |
| | | */ |
| | | abstract void getLDAPAttributes(Context c, Set<String> ldapAttributes); |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | |
| | | /** |
| | | * A name strategy is responsible for naming REST resources and LDAP entries. |
| | | */ |
| | | public abstract class NameStrategy { |
| | | /* |
| | | * This interface is an abstract class so that methods can be made package |
| | | * private until API is finalized. |
| | | */ |
| | | |
| | | NameStrategy() { |
| | | // Nothing to do. |
| | | } |
| | | |
| | | /** |
| | | * Returns a search request which can be used to obtain the specified REST |
| | | * resource. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param baseDN |
| | | * The search base DN. |
| | | * @param resourceId |
| | | * The resource ID. |
| | | * @return A search request which can be used to obtain the specified REST |
| | | * resource. |
| | | */ |
| | | abstract SearchRequest createSearchRequest(Context c, DN baseDN, String resourceId); |
| | | |
| | | /** |
| | | * Adds the name of any LDAP attribute required by this name strategy to the |
| | | * provided set. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param ldapAttributes |
| | | * The set into which any required LDAP attribute name should be |
| | | * put. |
| | | */ |
| | | abstract void getLDAPAttributes(Context c, Set<String> ldapAttributes); |
| | | |
| | | /** |
| | | * Retrieves the resource ID from the provided LDAP entry. Implementations |
| | | * may use the entry DN as well as any attributes in order to determine the |
| | | * resource ID. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param entry |
| | | * The LDAP entry from which the resource ID should be obtained. |
| | | * @return The resource ID. |
| | | */ |
| | | abstract String getResourceId(Context c, Entry entry); |
| | | |
| | | /** |
| | | * Sets the resource ID in the provided LDAP entry. Implementations are |
| | | * responsible for setting the entry DN as well as any attributes associated |
| | | * with the resource ID. |
| | | * |
| | | * @param c |
| | | * The context. |
| | | * @param baseDN |
| | | * The baseDN to use when constructing the entry's DN. |
| | | * @param resourceId |
| | | * The resource ID. |
| | | * @param entry |
| | | * The LDAP entry whose DN and resource ID attributes are to be |
| | | * set. |
| | | */ |
| | | abstract void setResourceId(Context c, DN baseDN, String resourceId, Entry entry); |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest; |
| | | import static org.forgerock.opendj.ldap.schema.CoreSchema.getEntryUUIDAttributeType; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.json.resource.CollectionResourceProvider; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.LinkedAttribute; |
| | | import org.forgerock.opendj.ldap.RDN; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | |
| | | /** |
| | | * Provides core factory methods and builders for constructing LDAP resource |
| | | * collections. |
| | | */ |
| | | public final class Rest2LDAP { |
| | | // @Checkstyle:off |
| | | |
| | | /** |
| | | * A builder for incrementally constructing LDAP resource collections. |
| | | */ |
| | | public static final class Builder { |
| | | private DN baseDN; // TODO: support template variables. |
| | | private Config config = Config.defaultConfig(); |
| | | private ConnectionFactory factory; |
| | | private final List<AttributeMapper> mappers = new LinkedList<AttributeMapper>(); |
| | | private MVCCStrategy mvccStrategy = mvccUsingEtag(); |
| | | private NameStrategy nameStrategy = nameByEntryUUID("uid"); |
| | | |
| | | Builder() { |
| | | // No implementation required. |
| | | } |
| | | |
| | | public Builder baseDN(final DN dn) { |
| | | ensureNotNull(dn); |
| | | this.baseDN = dn; |
| | | return this; |
| | | } |
| | | |
| | | public Builder baseDN(final String dn) { |
| | | ensureNotNull(dn); |
| | | this.baseDN = DN.valueOf(dn); |
| | | return this; |
| | | } |
| | | |
| | | public CollectionResourceProvider build() { |
| | | ensureNotNull(factory); |
| | | ensureNotNull(baseDN); |
| | | if (mappers.isEmpty()) { |
| | | throw new IllegalStateException("No mappings provided"); |
| | | } |
| | | return new LDAPCollectionResourceProvider(baseDN, mapOf(mappers), factory, config, |
| | | nameStrategy, mvccStrategy); |
| | | } |
| | | |
| | | public Builder config(final Config config) { |
| | | ensureNotNull(config); |
| | | this.config = config; |
| | | return this; |
| | | } |
| | | |
| | | public Builder factory(final ConnectionFactory factory) { |
| | | ensureNotNull(factory); |
| | | this.factory = factory; |
| | | return this; |
| | | } |
| | | |
| | | public Builder map(final AttributeMapper... mappers) { |
| | | ensureNotNull(mappers); |
| | | this.mappers.addAll(Arrays.asList(mappers)); |
| | | return this; |
| | | } |
| | | |
| | | public Builder map(final Collection<AttributeMapper> mappers) { |
| | | ensureNotNull(mappers); |
| | | this.mappers.addAll(mappers); |
| | | return this; |
| | | } |
| | | |
| | | public Builder with(final MVCCStrategy strategy) { |
| | | ensureNotNull(strategy); |
| | | this.mvccStrategy = strategy; |
| | | return this; |
| | | } |
| | | |
| | | public Builder with(final NameStrategy strategy) { |
| | | ensureNotNull(strategy); |
| | | this.nameStrategy = strategy; |
| | | return this; |
| | | } |
| | | } |
| | | |
| | | private static final class AttributeMVCCStrategy extends MVCCStrategy { |
| | | private final AttributeDescription ldapAttribute; |
| | | |
| | | private AttributeMVCCStrategy(final AttributeDescription ldapAttribute) { |
| | | this.ldapAttribute = ldapAttribute; |
| | | } |
| | | |
| | | @Override |
| | | String getRevisionFromEntry(final Context c, final Entry entry) { |
| | | return entry.parseAttribute(ldapAttribute).asString(); |
| | | } |
| | | |
| | | @Override |
| | | void getLDAPAttributes(final Context c, final Set<String> ldapAttributes) { |
| | | ldapAttributes.add(ldapAttribute.toString()); |
| | | } |
| | | } |
| | | |
| | | private static final class AttributeNameStrategy extends NameStrategy { |
| | | private final AttributeDescription dnAttribute; |
| | | private final AttributeDescription idAttribute; |
| | | private final boolean isServerProvided; |
| | | |
| | | private AttributeNameStrategy(final AttributeType dnAttribute, |
| | | final AttributeDescription idAttribute, final boolean isServerProvided) { |
| | | this.dnAttribute = AttributeDescription.create(dnAttribute); |
| | | if (dnAttribute.equals(idAttribute)) { |
| | | throw new IllegalArgumentException("DN and ID attributes must be different"); |
| | | } |
| | | this.idAttribute = ensureNotNull(idAttribute); |
| | | this.isServerProvided = isServerProvided; |
| | | } |
| | | |
| | | @Override |
| | | SearchRequest createSearchRequest(final Context c, final DN baseDN, final String resourceId) { |
| | | return newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter.equality(idAttribute |
| | | .toString(), resourceId)); |
| | | } |
| | | |
| | | @Override |
| | | void getLDAPAttributes(final Context c, final Set<String> ldapAttributes) { |
| | | ldapAttributes.add(idAttribute.toString()); |
| | | } |
| | | |
| | | @Override |
| | | String getResourceId(final Context c, final Entry entry) { |
| | | return entry.parseAttribute(idAttribute).asString(); |
| | | } |
| | | |
| | | @Override |
| | | void setResourceId(final Context c, final DN baseDN, final String resourceId, |
| | | final Entry entry) { |
| | | if (!isServerProvided) { |
| | | entry.addAttribute(new LinkedAttribute(idAttribute, ByteString.valueOf(resourceId))); |
| | | } |
| | | final String rdnValue = entry.parseAttribute(dnAttribute).asString(); |
| | | final RDN rdn = new RDN(dnAttribute.getAttributeType(), rdnValue); |
| | | entry.setName(baseDN.child(rdn)); |
| | | |
| | | } |
| | | } |
| | | |
| | | private static final class DNNameStrategy extends NameStrategy { |
| | | private final AttributeDescription attribute; |
| | | |
| | | private DNNameStrategy(final AttributeType attribute) { |
| | | this.attribute = AttributeDescription.create(attribute); |
| | | } |
| | | |
| | | @Override |
| | | SearchRequest createSearchRequest(final Context c, final DN baseDN, final String resourceId) { |
| | | return newSearchRequest(baseDN.child(rdn(resourceId)), SearchScope.BASE_OBJECT, c |
| | | .getConfig().trueFilter()); |
| | | } |
| | | |
| | | @Override |
| | | void getLDAPAttributes(final Context c, final Set<String> ldapAttributes) { |
| | | ldapAttributes.add(attribute.toString()); |
| | | } |
| | | |
| | | @Override |
| | | String getResourceId(final Context c, final Entry entry) { |
| | | return entry.parseAttribute(attribute).asString(); |
| | | } |
| | | |
| | | @Override |
| | | void setResourceId(final Context c, final DN baseDN, final String resourceId, |
| | | final Entry entry) { |
| | | entry.setName(baseDN.child(rdn(resourceId))); |
| | | entry.addAttribute(new LinkedAttribute(attribute, ByteString.valueOf(resourceId))); |
| | | } |
| | | |
| | | private RDN rdn(final String resourceId) { |
| | | return new RDN(attribute.getAttributeType(), resourceId); |
| | | } |
| | | |
| | | } |
| | | |
| | | public static SimpleAttributeMapper map(final AttributeDescription attribute) { |
| | | return map(attribute.toString(), attribute); |
| | | } |
| | | |
| | | public static SimpleAttributeMapper map(final String attribute) { |
| | | return map(attribute, attribute); |
| | | } |
| | | |
| | | public static SimpleAttributeMapper map(final String jsonAttribute, |
| | | final AttributeDescription ldapAttribute) { |
| | | return new SimpleAttributeMapper(jsonAttribute, ldapAttribute); |
| | | } |
| | | |
| | | public static SimpleAttributeMapper map(final String jsonAttribute, final String ldapAttribute) { |
| | | return map(jsonAttribute, AttributeDescription.valueOf(ldapAttribute)); |
| | | } |
| | | |
| | | public static AttributeMapper mapAllExcept(final String... attributes) { |
| | | return new DefaultAttributeMapper().excludeAttribute(attributes); |
| | | } |
| | | |
| | | public static AttributeMapper mapAllOf(final String... attributes) { |
| | | return new DefaultAttributeMapper().includeAttribute(attributes); |
| | | } |
| | | |
| | | public static AttributeMapper mapComplex(final String jsonAttribute, |
| | | final AttributeMapper... mappers) { |
| | | return mapComplex(jsonAttribute, Arrays.asList(mappers)); |
| | | } |
| | | |
| | | public static AttributeMapper mapComplex(final String jsonAttribute, |
| | | final Collection<AttributeMapper> mappers) { |
| | | return new ComplexAttributeMapper(jsonAttribute, mapOf(mappers)); |
| | | } |
| | | |
| | | public static AttributeMapper mapConstant(final String attribute, final Object attributeValue) { |
| | | return new ConstantAttributeMapper(attribute, attributeValue); |
| | | } |
| | | |
| | | public static MVCCStrategy mvccUsingAttribute(final AttributeDescription attribute) { |
| | | return new AttributeMVCCStrategy(attribute); |
| | | } |
| | | |
| | | public static MVCCStrategy mvccUsingAttribute(final String attribute) { |
| | | return mvccUsingAttribute(AttributeDescription.valueOf(attribute)); |
| | | } |
| | | |
| | | public static MVCCStrategy mvccUsingEtag() { |
| | | return mvccUsingAttribute("etag"); |
| | | } |
| | | |
| | | public static NameStrategy nameByClient(final AttributeType dnAttribute, |
| | | final AttributeDescription idAttribute) { |
| | | return new AttributeNameStrategy(dnAttribute, idAttribute, false); |
| | | } |
| | | |
| | | public static NameStrategy nameByClient(final String dnAttribute, final String idAttribute) { |
| | | return nameByClient(Schema.getDefaultSchema().getAttributeType(dnAttribute), |
| | | AttributeDescription.valueOf(idAttribute)); |
| | | } |
| | | |
| | | public static NameStrategy nameByDN(final AttributeType attribute) { |
| | | return new DNNameStrategy(attribute); |
| | | } |
| | | |
| | | public static NameStrategy nameByDN(final String attribute) { |
| | | return nameByDN(Schema.getDefaultSchema().getAttributeType(attribute)); |
| | | } |
| | | |
| | | public static NameStrategy nameByEntryUUID(final AttributeType dnAttribute) { |
| | | return nameByServer(dnAttribute, AttributeDescription.create(getEntryUUIDAttributeType())); |
| | | } |
| | | |
| | | public static NameStrategy nameByEntryUUID(final String dnAttribute) { |
| | | return nameByEntryUUID(Schema.getDefaultSchema().getAttributeType(dnAttribute)); |
| | | } |
| | | |
| | | public static NameStrategy nameByServer(final AttributeType dnAttribute, |
| | | final AttributeDescription idAttribute) { |
| | | return new AttributeNameStrategy(dnAttribute, idAttribute, true); |
| | | } |
| | | |
| | | public static NameStrategy nameByServer(final String dnAttribute, final String idAttribute) { |
| | | return nameByServer(Schema.getDefaultSchema().getAttributeType(dnAttribute), |
| | | AttributeDescription.valueOf(idAttribute)); |
| | | } |
| | | |
| | | public static Builder collection() { |
| | | return new Builder(); |
| | | } |
| | | |
| | | private static AttributeMapper mapOf(final Collection<AttributeMapper> mappers) { |
| | | return new CompositeAttributeMapper(mappers); |
| | | } |
| | | |
| | | private Rest2LDAP() { |
| | | // Prevent instantiation. |
| | | } |
| | | |
| | | } |
| | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toFilter; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | import org.forgerock.json.fluent.JsonValue; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | |
| | | * An attribute mapper which maps a single JSON attribute to a single LDAP |
| | | * attribute. |
| | | */ |
| | | public class SimpleAttributeMapper implements AttributeMapper { |
| | | public final class SimpleAttributeMapper extends AttributeMapper { |
| | | |
| | | private Function<ByteString, ?, Void> decoder = null; |
| | | private Object defaultValue = null; |
| | | private Collection<Object> defaultValues = Collections.emptySet(); |
| | | private boolean forceSingleValued = false; |
| | | |
| | | // private boolean isReadOnly = false; |
| | | private final String jsonAttributeName; |
| | | private final String ldapAttributeName; |
| | | private final AttributeDescription ldapAttributeName; |
| | | private final String normalizedJsonAttributeName; |
| | | |
| | | /** |
| | | * Creates a new simple attribute mapper which maps a single LDAP attribute |
| | | * to an entry. |
| | | * |
| | | * @param attributeName |
| | | * The name of the simple JSON and LDAP attribute. |
| | | */ |
| | | public SimpleAttributeMapper(final String attributeName) { |
| | | this(attributeName, attributeName); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new simple attribute mapper which maps a single LDAP attribute |
| | | * to an entry. |
| | | * |
| | | * @param jsonAttributeName |
| | | * The name of the simple JSON attribute. |
| | | * @param ldapAttributeName |
| | | * The name of the LDAP attribute. |
| | | */ |
| | | public SimpleAttributeMapper(final String jsonAttributeName, final String ldapAttributeName) { |
| | | SimpleAttributeMapper(final String jsonAttributeName, |
| | | final AttributeDescription ldapAttributeName) { |
| | | this.jsonAttributeName = jsonAttributeName; |
| | | this.ldapAttributeName = ldapAttributeName; |
| | | this.normalizedJsonAttributeName = toLowerCase(jsonAttributeName); |
| | |
| | | */ |
| | | public SimpleAttributeMapper defaultJSONValue(final Object defaultValue) { |
| | | this.defaultValue = defaultValue; |
| | | this.defaultValues = |
| | | defaultValue != null ? Collections.singleton(defaultValue) : Collections.emptySet(); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | if (jsonAttribute.isEmpty() || matches(jsonAttribute)) { |
| | | ldapAttributes.add(ldapAttributeName); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void getLDAPFilter(final Context c, final FilterType type, |
| | | final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, |
| | | final ResultHandler<Filter> h) { |
| | | if (matches(jsonAttribute)) { |
| | | h.handleResult(toFilter(c, type, ldapAttributeName, valueAssertion)); |
| | | } else { |
| | | // This attribute mapper cannot handle the provided filter component. |
| | | h.handleResult(null); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Prevents the LDAP attribute from being updated. |
| | | * |
| | | * @param readOnly |
| | |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | final Attribute a = e.getAttribute(ldapAttributeName); |
| | | if (a != null) { |
| | | final Function<ByteString, ?, Void> f = |
| | | decoder == null ? Functions.fixedFunction(byteStringToJson(), a) : decoder; |
| | | final Object value; |
| | | if (forceSingleValued || a.getAttributeDescription().getAttributeType().isSingleValue()) { |
| | | value = a.parse().as(f, defaultValue); |
| | | } else { |
| | | value = a.parse().asSetOf(f, defaultValue); |
| | | } |
| | | h.handleResult(Collections.singletonMap(jsonAttributeName, value)); |
| | | void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, |
| | | final Set<String> ldapAttributes) { |
| | | if (jsonAttribute.isEmpty() || matches(jsonAttribute)) { |
| | | ldapAttributes.add(ldapAttributeName.toString()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute, |
| | | final String operator, final Object valueAssertion, final ResultHandler<Filter> h) { |
| | | if (matches(jsonAttribute)) { |
| | | h.handleResult(toFilter(c, type, ldapAttributeName.toString(), valueAssertion)); |
| | | } else { |
| | | // This attribute mapper cannot handle the provided filter component. |
| | | h.handleResult(null); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) { |
| | | final Function<ByteString, ?, Void> f = |
| | | decoder == null ? Functions.fixedFunction(byteStringToJson(), ldapAttributeName) |
| | | : decoder; |
| | | final Object value; |
| | | if (forceSingleValued || ldapAttributeName.getAttributeType().isSingleValue()) { |
| | | value = e.parseAttribute(ldapAttributeName).as(f, defaultValue); |
| | | } else { |
| | | value = e.parseAttribute(ldapAttributeName).asSetOf(f, defaultValues); |
| | | } |
| | | h.handleResult(Collections.singletonMap(jsonAttributeName, value)); |
| | | } |
| | | |
| | | @Override |
| | | void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.json.resource.ResultHandler; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Function; |
| | |
| | | } |
| | | |
| | | // @Checkstyle:off |
| | | private static final Function<ByteString, Object, Attribute> BYTESTRING_TO_JSON = |
| | | new Function<ByteString, Object, Attribute>() { |
| | | private static final Function<ByteString, Object, AttributeDescription> BYTESTRING_TO_JSON = |
| | | new Function<ByteString, Object, AttributeDescription>() { |
| | | @Override |
| | | public Object apply(final ByteString value, final Attribute a) { |
| | | final Syntax syntax = |
| | | a.getAttributeDescription().getAttributeType().getSyntax(); |
| | | public Object apply(final ByteString value, final AttributeDescription ad) { |
| | | final Syntax syntax = ad.getAttributeType().getSyntax(); |
| | | if (syntax.equals(getBooleanSyntax())) { |
| | | return Functions.byteStringToBoolean().apply(value, null); |
| | | } else if (syntax.equals(getIntegerSyntax())) { |
| | |
| | | } |
| | | |
| | | static Object attributeToJson(final Attribute a) { |
| | | final Function<ByteString, Object, Void> f = Functions.fixedFunction(BYTESTRING_TO_JSON, a); |
| | | final Function<ByteString, Object, Void> f = |
| | | Functions.fixedFunction(BYTESTRING_TO_JSON, a.getAttributeDescription()); |
| | | final boolean isSingleValued = |
| | | a.getAttributeDescription().getAttributeType().isSingleValue(); |
| | | return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f)); |
| | |
| | | |
| | | // @Checkstyle:on |
| | | |
| | | static Function<ByteString, Object, Attribute> byteStringToJson() { |
| | | static Function<ByteString, Object, AttributeDescription> byteStringToJson() { |
| | | return BYTESTRING_TO_JSON; |
| | | } |
| | | |
| | |
| | | |
| | | import static org.forgerock.json.resource.Resources.newInternalConnectionFactory; |
| | | import static org.forgerock.opendj.ldap.Connections.newAuthenticatedConnectionFactory; |
| | | import static org.forgerock.opendj.rest2ldap.Rest2LDAP.collection; |
| | | import static org.forgerock.opendj.rest2ldap.Rest2LDAP.map; |
| | | import static org.forgerock.opendj.rest2ldap.Rest2LDAP.mapAllOf; |
| | | import static org.forgerock.opendj.rest2ldap.Rest2LDAP.mapComplex; |
| | | |
| | | import java.util.logging.Logger; |
| | | |
| | | import org.forgerock.json.resource.CollectionResourceProvider; |
| | | import org.forgerock.json.resource.Router; |
| | | import org.forgerock.json.resource.servlet.HttpServlet; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Functions; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | |
| | | Requests.newSimpleBindRequest("cn=directory manager", "password" |
| | | .toCharArray())); |
| | | |
| | | // Create user resource. |
| | | final AttributeMapper userMapper = |
| | | new CompositeAttributeMapper().addMapper( |
| | | new SimpleAttributeMapper("id", "entryUUID").singleValued(true)).addMapper( |
| | | new DefaultAttributeMapper().includeAttribute("uid", "isMemberOf", |
| | | "modifyTimestamp")).addMapper( |
| | | new ComplexAttributeMapper("name", new DefaultAttributeMapper() |
| | | .includeAttribute("cn", "sn", "givenName"))).addMapper( |
| | | new ComplexAttributeMapper("contactInformation", |
| | | new CompositeAttributeMapper().addMapper( |
| | | new SimpleAttributeMapper("telephoneNumber").decoder( |
| | | Functions.byteStringToString()).singleValued(true)) |
| | | .addMapper( |
| | | new SimpleAttributeMapper("emailAddress", "mail") |
| | | .singleValued(true)))); |
| | | |
| | | final LDAPCollectionResourceProvider userResource = |
| | | new LDAPCollectionResourceProvider(DN.valueOf("ou=people,dc=example,dc=com"), |
| | | userMapper, ldapFactory, Config.defaultConfig()); |
| | | |
| | | // Create group resource. |
| | | final AttributeMapper groupMapper = |
| | | new DefaultAttributeMapper().includeAttribute("cn", "ou", "description", |
| | | "uniquemember"); |
| | | |
| | | final LDAPCollectionResourceProvider groupResource = |
| | | new LDAPCollectionResourceProvider(DN.valueOf("ou=groups,dc=example,dc=com"), |
| | | groupMapper, ldapFactory, Config.defaultConfig()); |
| | | |
| | | // Create the router. |
| | | final Router router = new Router(); |
| | | router.addRoute("/users", userResource); |
| | | router.addRoute("/groups", groupResource); |
| | | |
| | | // Create user resource. |
| | | CollectionResourceProvider users = |
| | | collection().factory(ldapFactory).baseDN("ou=people,dc=example,dc=com").map( |
| | | map("id", "entryUUID").singleValued(true), |
| | | mapAllOf("uid", "isMemberOf", "modifyTimestamp"), |
| | | mapComplex("name", mapAllOf("cn", "sn", "givenName")), |
| | | mapComplex("contactInformation", map("telephoneNumber").decoder( |
| | | Functions.byteStringToString()).singleValued(true), map( |
| | | "emailAddress", "mail").singleValued(true))).build(); |
| | | router.addRoute("/users", users); |
| | | |
| | | // Create group resource. |
| | | CollectionResourceProvider groups = |
| | | collection().factory(ldapFactory).baseDN("ou=groups,dc=example,dc=com").map( |
| | | mapAllOf("cn", "ou", "description", "uniquemember")).build(); |
| | | router.addRoute("/groups", groups); |
| | | |
| | | final org.forgerock.json.resource.ConnectionFactory resourceFactory = |
| | | newInternalConnectionFactory(router); |