OPENDJ-3160 Pass Context through to PropertyMapper methods
Pass in the Context instead of the Connection so that property mapper
implementations can access additional contextual information such as the
routing context and URL template parameters.
| | |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.LinkedAttribute; |
| | | import org.forgerock.opendj.ldap.Modification; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.Function; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.Promises; |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Attribute>, ResourceException> create(final Connection connection, |
| | | Promise<List<Attribute>, ResourceException> create(final Context context, |
| | | final Resource resource, final JsonPointer path, |
| | | final JsonValue v) { |
| | | return getNewLdapAttributes(connection, resource, path, v).then( |
| | | return getNewLdapAttributes(context, resource, path, v).then( |
| | | new Function<Attribute, List<Attribute>, ResourceException>() { |
| | | @Override |
| | | public List<Attribute> apply(Attribute newLDAPAttribute) throws ResourceException { |
| | |
| | | ldapAttributes.add(ldapAttributeName.toString()); |
| | | } |
| | | |
| | | abstract Promise<Attribute, ResourceException> getNewLdapAttributes(Connection connection, Resource resource, |
| | | abstract Promise<Attribute, ResourceException> getNewLdapAttributes(Context context, Resource resource, |
| | | JsonPointer path, List<Object> newValues); |
| | | |
| | | abstract T getThis(); |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> patch(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> patch(final Context context, final Resource resource, |
| | | final JsonPointer path, final PatchOperation operation) { |
| | | try { |
| | | final JsonPointer field = operation.getField(); |
| | |
| | | singletonList(new Modification(modType, emptyAttribute(ldapAttributeName)))); |
| | | } |
| | | } else { |
| | | return getNewLdapAttributes(connection, resource, path, newValues) |
| | | return getNewLdapAttributes(context, resource, path, newValues) |
| | | .then(new Function<Attribute, List<Modification>, ResourceException>() { |
| | | @Override |
| | | public List<Modification> apply(final Attribute value) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> update(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> update(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e, final JsonValue v) { |
| | | return getNewLdapAttributes(connection, resource, path, v).then( |
| | | return getNewLdapAttributes(context, resource, path, v).then( |
| | | new Function<Attribute, List<Modification>, ResourceException>() { |
| | | @Override |
| | | public List<Modification> apply(final Attribute newLDAPAttribute) throws ResourceException { |
| | |
| | | } |
| | | } |
| | | |
| | | private Promise<Attribute, ResourceException> getNewLdapAttributes(final Connection connection, |
| | | private Promise<Attribute, ResourceException> getNewLdapAttributes(final Context context, |
| | | final Resource resource, final JsonPointer path, |
| | | final JsonValue v) { |
| | | try { |
| | |
| | | // Skip sub-class implementation if there are no values. |
| | | return newResultPromise(emptyAttribute(ldapAttributeName)); |
| | | } else { |
| | | return getNewLdapAttributes(connection, resource, path, newValues); |
| | | return getNewLdapAttributes(context, resource, path, newValues); |
| | | } |
| | | } catch (final Exception e) { |
| | | return asResourceException(e).asPromise(); |
| | |
| | | import org.forgerock.json.resource.PatchOperation; |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Modification; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.promise.Promise; |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Attribute>, ResourceException> create(final Connection connection, |
| | | Promise<List<Attribute>, ResourceException> create(final Context context, |
| | | final Resource resource, final JsonPointer path, |
| | | final JsonValue v) { |
| | | if (!isNullOrEmpty(v) && !v.getObject().equals(value.getObject())) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<Filter, ResourceException> getLdapFilter(final Connection connection, final Resource resource, |
| | | Promise<Filter, ResourceException> getLdapFilter(final Context context, final Resource resource, |
| | | final JsonPointer path, final JsonPointer subPath, |
| | | final FilterType type, final String operator, |
| | | final Object valueAssertion) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> patch(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> patch(final Context context, final Resource resource, |
| | | final JsonPointer path, final PatchOperation operation) { |
| | | return newBadRequestException(ERR_PATCH_READ_ONLY_FIELD.get(path)).asPromise(); |
| | | } |
| | | |
| | | @Override |
| | | Promise<JsonValue, ResourceException> read(final Connection connection, final Resource resource, |
| | | Promise<JsonValue, ResourceException> read(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e) { |
| | | return newResultPromise(value.copy()); |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> update(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> update(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e, final JsonValue v) { |
| | | if (!isNullOrEmpty(v) && !v.getObject().equals(value.getObject())) { |
| | | return newBadRequestException(ERR_MODIFY_READ_ONLY_FIELD.get("update", path)).asPromise(); |
| | |
| | | import org.forgerock.json.resource.PatchOperation; |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Modification; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.Function; |
| | | import org.forgerock.util.Pair; |
| | | import org.forgerock.util.promise.Promise; |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Attribute>, ResourceException> create(final Connection connection, |
| | | Promise<List<Attribute>, ResourceException> create(final Context context, |
| | | final Resource resource, final JsonPointer path, |
| | | final JsonValue v) { |
| | | try { |
| | |
| | | for (final Map.Entry<String, Object> me : v.asMap().entrySet()) { |
| | | final Mapping mapping = getMapping(me.getKey()); |
| | | final JsonValue subValue = new JsonValue(me.getValue()); |
| | | promises.add(mapping.mapper.create(connection, resource, path.child(me.getKey()), |
| | | promises.add(mapping.mapper.create(context, resource, path.child(me.getKey()), |
| | | subValue)); |
| | | } |
| | | } |
| | | |
| | | // Invoke mappings for which there were no values provided. |
| | | for (final Mapping mapping : missingMappings.values()) { |
| | | promises.add(mapping.mapper.create(connection, resource, path.child(mapping.name), null)); |
| | | promises.add(mapping.mapper.create(context, resource, path.child(mapping.name), null)); |
| | | } |
| | | |
| | | return Promises.when(promises) |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<Filter, ResourceException> getLdapFilter(final Connection connection, final Resource resource, |
| | | Promise<Filter, ResourceException> getLdapFilter(final Context context, final Resource resource, |
| | | final JsonPointer path, final JsonPointer subPath, |
| | | final FilterType type, final String operator, |
| | | final Object valueAssertion) { |
| | | final Mapping mapping = getMappingOrNull(subPath); |
| | | if (mapping != null) { |
| | | return mapping.mapper.getLdapFilter(connection, |
| | | return mapping.mapper.getLdapFilter(context, |
| | | resource, |
| | | path.child(subPath.get(0)), |
| | | subPath.relativePointer(), |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> patch(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> patch(final Context context, final Resource resource, |
| | | final JsonPointer path, final PatchOperation operation) { |
| | | try { |
| | | final JsonPointer field = operation.getField(); |
| | |
| | | final JsonValue subValue = new JsonValue(me.getValue()); |
| | | final PatchOperation subOperation = |
| | | operation(operation.getOperation(), field /* empty */, subValue); |
| | | promises.add(mapping.mapper.patch(connection, resource, path.child(me.getKey()), subOperation)); |
| | | promises.add(mapping.mapper.patch(context, resource, path.child(me.getKey()), subOperation)); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | final PatchOperation subOperation = |
| | | operation(operation.getOperation(), field.relativePointer(), v); |
| | | return mapping.mapper.patch(connection, resource, path.child(fieldName), subOperation); |
| | | return mapping.mapper.patch(context, resource, path.child(fieldName), subOperation); |
| | | } |
| | | } catch (final Exception e) { |
| | | return asResourceException(e).asPromise(); |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<JsonValue, ResourceException> read(final Connection connection, final Resource resource, |
| | | Promise<JsonValue, ResourceException> read(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e) { |
| | | /* |
| | | * Use an accumulator which will aggregate the results from the |
| | |
| | | new ArrayList<>(mappings.size()); |
| | | |
| | | for (final Mapping mapping : mappings.values()) { |
| | | promises.add(mapping.mapper.read(connection, resource, path.child(mapping.name), e) |
| | | promises.add(mapping.mapper.read(context, resource, path.child(mapping.name), e) |
| | | .then(toProperty(mapping.name))); |
| | | } |
| | | |
| | |
| | | } |
| | | // This attribute needs to be mapped. |
| | | final SimplePropertyMapper mapper = simple(attribute.getAttributeDescription()); |
| | | promises.add(mapper.read(connection, resource, path.child(attributeName), e) |
| | | promises.add(mapper.read(context, resource, path.child(attributeName), e) |
| | | .then(toProperty(attributeName))); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> update(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> update(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e, final JsonValue v) { |
| | | try { |
| | | // First check that the JSON value is an object and that the fields it contains are known by this mapper. |
| | |
| | | for (final Map.Entry<String, Object> me : v.asMap().entrySet()) { |
| | | final Mapping mapping = getMapping(me.getKey()); |
| | | final JsonValue subValue = new JsonValue(me.getValue()); |
| | | promises.add(mapping.mapper.update(connection, resource, path.child(me.getKey()), e, subValue)); |
| | | promises.add(mapping.mapper.update(context, resource, path.child(me.getKey()), e, subValue)); |
| | | } |
| | | } |
| | | |
| | | // Invoke mappings for which there were no values provided. |
| | | for (final Mapping mapping : missingMappings.values()) { |
| | | promises.add(mapping.mapper.update(connection, resource, path.child(mapping.name), e, null)); |
| | | promises.add(mapping.mapper.update(context, resource, path.child(mapping.name), e, null)); |
| | | } |
| | | |
| | | return Promises.when(promises) |
| | |
| | | import org.forgerock.json.resource.PatchOperation; |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Modification; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.promise.Promise; |
| | | |
| | | /** An property mapper is responsible for converting JSON values to and from LDAP attributes. */ |
| | |
| | | * action in this case, perhaps by substituting default LDAP values, or by |
| | | * returning a failed promise with an appropriate {@link ResourceException}. |
| | | * |
| | | * @param connection |
| | | * The LDAP connection to use to perform the operation. |
| | | * @param context The request context. |
| | | * @param resource The exact type of resource being created. |
| | | * @param path |
| | | * The pointer from the root of the JSON resource to this |
| | |
| | | * in the resource. |
| | | * @return A {@link Promise} containing the result of the operation. |
| | | */ |
| | | abstract Promise<List<Attribute>, ResourceException> create(Connection connection, Resource resource, |
| | | abstract Promise<List<Attribute>, ResourceException> create(Context context, Resource resource, |
| | | JsonPointer path, JsonValue v); |
| | | |
| | | /** |
| | |
| | | * promise must be returned with an appropriate {@link ResourceException} |
| | | * indicating the problem which occurred. |
| | | * |
| | | * @param connection |
| | | * The LDAP connection to use to perform the operation. |
| | | * @param context The request context. |
| | | * @param resource The type of resource being queried. |
| | | * @param path |
| | | * The pointer from the root of the JSON resource to this |
| | |
| | | * {@link FilterType#PRESENT}. |
| | | * @return A {@link Promise} containing the result of the operation. |
| | | */ |
| | | abstract Promise<Filter, ResourceException> getLdapFilter(Connection connection, Resource resource, |
| | | abstract Promise<Filter, ResourceException> getLdapFilter(Context context, Resource resource, |
| | | JsonPointer path, JsonPointer subPath, FilterType type, |
| | | String operator, Object valueAssertion); |
| | | |
| | |
| | | * a promise once the transformation has completed. This method is invoked |
| | | * when a REST resource is modified using a patch request. |
| | | * |
| | | * @param connection |
| | | * The LDAP connection to use to perform the operation. |
| | | * @param context The request context. |
| | | * @param resource The exact type of resource being patched. |
| | | * @param path |
| | | * The pointer from the root of the JSON resource to this |
| | |
| | | * with this mapper have been targeted. |
| | | * @return A {@link Promise} containing the result of the operation. |
| | | */ |
| | | abstract Promise<List<Modification>, ResourceException> patch(Connection connection, Resource resource, |
| | | abstract Promise<List<Modification>, ResourceException> patch(Context context, Resource resource, |
| | | JsonPointer path, PatchOperation operation); |
| | | |
| | | /** |
| | |
| | | * they contain unexpected content, then a failed promise must be returned |
| | | * with an appropriate exception indicating the problem which occurred. |
| | | * |
| | | * @param connection |
| | | * The LDAP connection to use to perform the operation. |
| | | * @param context The request context. |
| | | * @param resource The exact type of resource being read. |
| | | * @param path |
| | | * The pointer from the root of the JSON resource to this |
| | |
| | | * The LDAP entry to be converted to JSON. |
| | | * @return A {@link Promise} containing the result of the operation. |
| | | */ |
| | | abstract Promise<JsonValue, ResourceException> read(Connection connection, Resource resource, |
| | | abstract Promise<JsonValue, ResourceException> read(Context context, Resource resource, |
| | | JsonPointer path, Entry e); |
| | | |
| | | /** |
| | |
| | | * action in this case, perhaps by substituting default LDAP values, or by |
| | | * returning a failed promise with an appropriate {@link ResourceException}. |
| | | * |
| | | * @param connection |
| | | * The LDAP connection to use to perform the operation. |
| | | * @param context The request context. |
| | | * @param resource The exact type of resource being updated. |
| | | * @param v |
| | | * The JSON value to be converted to LDAP attributes, which may |
| | |
| | | * in the resource. |
| | | * @return A {@link Promise} containing the result of the operation. |
| | | */ |
| | | abstract Promise<List<Modification>, ResourceException> update(Connection connection, Resource resource, |
| | | abstract Promise<List<Modification>, ResourceException> update(Context context, Resource resource, |
| | | JsonPointer path, Entry e, JsonValue v); |
| | | |
| | | // TODO: methods for obtaining schema information (e.g. name, description, type information). |
| | |
| | | import static org.forgerock.opendj.ldap.LdapException.newLdapException; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest; |
| | | import static org.forgerock.opendj.rest2ldap.Rest2Ldap.asResourceException; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.connectionFrom; |
| | | import static org.forgerock.util.Reject.checkNotNull; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.newBadRequestException; |
| | | import static org.forgerock.util.promise.Promises.newResultPromise; |
| | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultReference; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.AsyncFunction; |
| | | import org.forgerock.util.Function; |
| | | import org.forgerock.util.promise.ExceptionHandler; |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<Filter, ResourceException> getLdapFilter(final Connection connection, final Resource resource, |
| | | Promise<Filter, ResourceException> getLdapFilter(final Context context, final Resource resource, |
| | | final JsonPointer path, final JsonPointer subPath, |
| | | final FilterType type, final String operator, |
| | | final Object valueAssertion) { |
| | | return mapper.getLdapFilter(connection, resource, path, subPath, type, operator, valueAssertion) |
| | | return mapper.getLdapFilter(context, resource, path, subPath, type, operator, valueAssertion) |
| | | .thenAsync(new AsyncFunction<Filter, Filter, ResourceException>() { |
| | | @Override |
| | | public Promise<Filter, ResourceException> apply(final Filter result) { |
| | |
| | | final SearchRequest request = createSearchRequest(result); |
| | | final List<Filter> subFilters = new LinkedList<>(); |
| | | |
| | | return connection.searchAsync(request, new SearchResultHandler() { |
| | | return connectionFrom(context).searchAsync(request, new SearchResultHandler() { |
| | | @Override |
| | | public boolean handleEntry(final SearchResultEntry entry) { |
| | | if (subFilters.size() < SEARCH_MAX_CANDIDATES) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<Attribute, ResourceException> getNewLdapAttributes(final Connection connection, final Resource resource, |
| | | Promise<Attribute, ResourceException> getNewLdapAttributes(final Context context, final Resource resource, |
| | | final JsonPointer path, final List<Object> newValues) { |
| | | /* |
| | | * For each value use the subordinate mapper to obtain the LDAP primary |
| | |
| | | final PromiseImpl<Attribute, ResourceException> promise = PromiseImpl.create(); |
| | | |
| | | for (final Object value : newValues) { |
| | | mapper.create(connection, resource, path, new JsonValue(value)) |
| | | mapper.create(context, resource, path, new JsonValue(value)) |
| | | .thenOnResult(new ResultHandler<List<Attribute>>() { |
| | | @Override |
| | | public void handleResult(List<Attribute> result) { |
| | |
| | | final ByteString primaryKeyValue = primaryKeyAttribute.firstValue(); |
| | | final Filter filter = Filter.equality(primaryKey.toString(), primaryKeyValue); |
| | | final SearchRequest search = createSearchRequest(filter); |
| | | connection.searchSingleEntryAsync(search) |
| | | connectionFrom(context).searchSingleEntryAsync(search) |
| | | .thenOnResult(new ResultHandler<SearchResultEntry>() { |
| | | @Override |
| | | public void handleResult(final SearchResultEntry result) { |
| | |
| | | |
| | | @SuppressWarnings("fallthrough") |
| | | @Override |
| | | Promise<JsonValue, ResourceException> read(final Connection connection, final Resource resource, |
| | | Promise<JsonValue, ResourceException> read(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e) { |
| | | final Set<DN> dns = e.parseAttribute(ldapAttributeName).usingSchema(schema).asSetOfDN(); |
| | | switch (dns.size()) { |
| | |
| | | case 1: |
| | | if (attributeIsSingleValued()) { |
| | | try { |
| | | return readEntry(connection, resource, path, dns.iterator().next()); |
| | | return readEntry(context, resource, path, dns.iterator().next()); |
| | | } catch (final Exception ex) { |
| | | // The LDAP attribute could not be decoded. |
| | | return Promises.newExceptionPromise(asResourceException(ex)); |
| | |
| | | try { |
| | | final List<Promise<JsonValue, ResourceException>> promises = new ArrayList<>(dns.size()); |
| | | for (final DN dn : dns) { |
| | | promises.add(readEntry(connection, resource, path, dn)); |
| | | promises.add(readEntry(context, resource, path, dn)); |
| | | } |
| | | return Promises.when(promises) |
| | | .then(new Function<List<JsonValue>, JsonValue, ResourceException>() { |
| | |
| | | } |
| | | |
| | | private Promise<JsonValue, ResourceException> readEntry( |
| | | final Connection connection, final Resource resource, final JsonPointer path, final DN dn) { |
| | | final Context context, final Resource resource, final JsonPointer path, final DN dn) { |
| | | final Set<String> requestedLDAPAttributes = new LinkedHashSet<>(); |
| | | mapper.getLdapAttributes(path, new JsonPointer(), requestedLDAPAttributes); |
| | | |
| | |
| | | final String[] attributes = requestedLDAPAttributes.toArray(new String[requestedLDAPAttributes.size()]); |
| | | final SearchRequest request = newSearchRequest(dn, SearchScope.BASE_OBJECT, searchFilter, attributes); |
| | | |
| | | return connection |
| | | return connectionFrom(context) |
| | | .searchSingleEntryAsync(request) |
| | | .thenAsync(new AsyncFunction<SearchResultEntry, JsonValue, ResourceException>() { |
| | | @Override |
| | | public Promise<JsonValue, ResourceException> apply(final SearchResultEntry result) { |
| | | return mapper.read(connection, resource, path, result); |
| | | return mapper.read(context, resource, path, result); |
| | | } |
| | | }, new AsyncFunction<LdapException, JsonValue, ResourceException>() { |
| | | @Override |
| | |
| | | import org.forgerock.json.resource.ResourceException; |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.Modification; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.promise.Promise; |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Attribute>, ResourceException> create(final Connection connection, |
| | | Promise<List<Attribute>, ResourceException> create(final Context context, |
| | | final Resource resource, final JsonPointer path, |
| | | final JsonValue v) { |
| | | return newResultPromise(singletonList(resource.getObjectClassAttribute())); |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<Filter, ResourceException> getLdapFilter(final Connection connection, final Resource resource, |
| | | Promise<Filter, ResourceException> getLdapFilter(final Context context, final Resource resource, |
| | | final JsonPointer path, final JsonPointer subPath, |
| | | final FilterType type, final String operator, |
| | | final Object valueAssertion) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> patch(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> patch(final Context context, final Resource resource, |
| | | final JsonPointer path, final PatchOperation operation) { |
| | | return newBadRequestException(ERR_PATCH_READ_ONLY_FIELD.get(path)).asPromise(); |
| | | } |
| | | |
| | | @Override |
| | | Promise<JsonValue, ResourceException> read(final Connection connection, final Resource resource, |
| | | Promise<JsonValue, ResourceException> read(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e) { |
| | | return newResultPromise(new JsonValue(resource.getResourceId())); |
| | | } |
| | | |
| | | @Override |
| | | Promise<List<Modification>, ResourceException> update(final Connection connection, final Resource resource, |
| | | Promise<List<Modification>, ResourceException> update(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e, final JsonValue v) { |
| | | if (!isNullOrEmpty(v) && !v.getObject().equals(resource.getResourceId())) { |
| | | return newBadRequestException(ERR_MODIFY_READ_ONLY_FIELD.get("update", path)).asPromise(); |
| | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.Function; |
| | | import org.forgerock.util.promise.NeverThrowsException; |
| | | import org.forgerock.util.promise.Promise; |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<Filter, ResourceException> getLdapFilter(final Connection connection, final Resource resource, |
| | | Promise<Filter, ResourceException> getLdapFilter(final Context context, final Resource resource, |
| | | final JsonPointer path, final JsonPointer subPath, |
| | | final FilterType type, final String operator, |
| | | final Object valueAssertion) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | Promise<Attribute, ResourceException> getNewLdapAttributes(final Connection connection, final Resource resource, |
| | | Promise<Attribute, ResourceException> getNewLdapAttributes(final Context context, final Resource resource, |
| | | final JsonPointer path, final List<Object> newValues) { |
| | | try { |
| | | return newResultPromise(jsonToAttribute(newValues, ldapAttributeName, encoder())); |
| | |
| | | |
| | | @SuppressWarnings("fallthrough") |
| | | @Override |
| | | Promise<JsonValue, ResourceException> read(final Connection connection, final Resource resource, |
| | | Promise<JsonValue, ResourceException> read(final Context context, final Resource resource, |
| | | final JsonPointer path, final Entry e) { |
| | | try { |
| | | final Set<Object> s = e.parseAttribute(ldapAttributeName).asSetOf(decoder(), defaultJsonValues); |
| | |
| | | import static org.forgerock.opendj.ldap.SearchScope.SINGLE_LEVEL; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.CONTROLS; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.connectionFrom; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.newBadRequestException; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.newNotSupportedException; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.toFilter; |
| | |
| | | // Now build the LDAP representation and add it. |
| | | final Connection connection = connectionFrom(context); |
| | | return subType.getPropertyMapper() |
| | | .create(connection, subType, ROOT, request.getContent()) |
| | | .create(context, subType, ROOT, request.getContent()) |
| | | .thenAsync(new AsyncFunction<List<Attribute>, ResourceResponse, ResourceException>() { |
| | | @Override |
| | | public Promise<ResourceResponse, ResourceException> apply(final List<Attribute> attributes) { |
| | |
| | | } |
| | | return connection.addAsync(addRequest) |
| | | .thenCatchAsync(lazilyAddGlueEntry(connection, addRequest)) |
| | | .thenAsync(encodeUpdateResourceResponse(connection, subType), |
| | | .thenAsync(encodeUpdateResourceResponse(context, subType), |
| | | adaptLdapException(ResourceResponse.class)); |
| | | } |
| | | }); |
| | |
| | | }; |
| | | } |
| | | |
| | | private Connection connectionFrom(final Context context) { |
| | | return context.asContext(AuthenticatedConnectionContext.class).getConnection(); |
| | | } |
| | | |
| | | Promise<ResourceResponse, ResourceException> delete( |
| | | final Context context, final String resourceId, final DeleteRequest request) { |
| | | final Connection connection = connectionFrom(context); |
| | |
| | | return connection.applyChangeAsync(deleteRequest) |
| | | .thenCatchAsync(deleteSubtreeWithoutUsingSubtreeDeleteControl(connection, |
| | | deleteRequest)) |
| | | .thenAsync(encodeUpdateResourceResponse(connection, dnAndType.getType()), |
| | | .thenAsync(encodeUpdateResourceResponse(context, dnAndType.getType()), |
| | | adaptLdapException(ResourceResponse.class)); |
| | | } |
| | | }); |
| | |
| | | final Resource subType = dnAndType.getType(); |
| | | final PropertyMapper propertyMapper = subType.getPropertyMapper(); |
| | | for (final PatchOperation operation : request.getPatchOperations()) { |
| | | promises.add(propertyMapper.patch(connection, subType, ROOT, operation)); |
| | | promises.add(propertyMapper.patch(context, subType, ROOT, operation)); |
| | | } |
| | | return when(promises); |
| | | } |
| | |
| | | if (modifyRequest.getModifications().isEmpty()) { |
| | | // This patch is a no-op so just read the entry and check its version. |
| | | return connection.readEntryAsync(dnAndType.getDn(), attributes) |
| | | .thenAsync(encodeEmptyPatchResourceResponse(connection, subType, request), |
| | | .thenAsync(encodeEmptyPatchResourceResponse(context, subType, request), |
| | | adaptLdapException(ResourceResponse.class)); |
| | | } else { |
| | | // Add controls and perform the modify request. |
| | |
| | | } |
| | | addAssertionControl(modifyRequest, request.getRevision()); |
| | | return connection.applyChangeAsync(modifyRequest) |
| | | .thenAsync(encodeUpdateResourceResponse(connection, subType), |
| | | .thenAsync(encodeUpdateResourceResponse(context, subType), |
| | | adaptLdapException(ResourceResponse.class)); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | private AsyncFunction<Entry, ResourceResponse, ResourceException> encodeEmptyPatchResourceResponse( |
| | | final Connection connection, final Resource resource, final PatchRequest request) { |
| | | final Context context, final Resource resource, final PatchRequest request) { |
| | | return new AsyncFunction<Entry, ResourceResponse, ResourceException>() { |
| | | @Override |
| | | public Promise<ResourceResponse, ResourceException> apply(Entry entry) throws ResourceException { |
| | | try { |
| | | ensureMvccVersionMatches(entry, request.getRevision()); |
| | | return encodeResourceResponse(connection, resource, entry); |
| | | return encodeResourceResponse(context, resource, entry); |
| | | } catch (final Exception e) { |
| | | return asResourceException(e).asPromise(); |
| | | } |
| | |
| | | |
| | | Promise<QueryResponse, ResourceException> query( |
| | | final Context context, final QueryRequest request, final QueryResourceHandler resourceHandler) { |
| | | final Connection connection = connectionFrom(context); |
| | | return getLdapFilter(connection, request.getQueryFilter()) |
| | | .thenAsync(runQuery(request, resourceHandler, connection)); |
| | | return getLdapFilter(context, request.getQueryFilter()) |
| | | .thenAsync(runQuery(context, request, resourceHandler)); |
| | | } |
| | | |
| | | // FIXME: supporting assertions against sub-type properties. |
| | | private Promise<Filter, ResourceException> getLdapFilter( |
| | | final Connection connection, final QueryFilter<JsonPointer> queryFilter) { |
| | | final Context context, final QueryFilter<JsonPointer> queryFilter) { |
| | | if (queryFilter == null) { |
| | | return new BadRequestException(ERR_QUERY_BY_ID_OR_EXPRESSION_NOT_SUPPORTED.get().toString()).asPromise(); |
| | | } |
| | |
| | | public Promise<Filter, ResourceException> visitContainsFilter( |
| | | final Void unused, final JsonPointer field, final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, CONTAINS, null, valueAssertion); |
| | | context, resource, ROOT, field, CONTAINS, null, valueAssertion); |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Filter, ResourceException> visitEqualsFilter( |
| | | final Void unused, final JsonPointer field, final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, EQUAL_TO, null, valueAssertion); |
| | | context, resource, ROOT, field, EQUAL_TO, null, valueAssertion); |
| | | } |
| | | |
| | | @Override |
| | |
| | | final String operator, |
| | | final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, EXTENDED, operator, valueAssertion); |
| | | context, resource, ROOT, field, EXTENDED, operator, valueAssertion); |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Filter, ResourceException> visitGreaterThanFilter( |
| | | final Void unused, final JsonPointer field, final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, GREATER_THAN, null, valueAssertion); |
| | | context, resource, ROOT, field, GREATER_THAN, null, valueAssertion); |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Filter, ResourceException> visitGreaterThanOrEqualToFilter( |
| | | final Void unused, final JsonPointer field, final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, GREATER_THAN_OR_EQUAL_TO, null, valueAssertion); |
| | | context, resource, ROOT, field, GREATER_THAN_OR_EQUAL_TO, null, valueAssertion); |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Filter, ResourceException> visitLessThanFilter( |
| | | final Void unused, final JsonPointer field, final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, LESS_THAN, null, valueAssertion); |
| | | context, resource, ROOT, field, LESS_THAN, null, valueAssertion); |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Filter, ResourceException> visitLessThanOrEqualToFilter( |
| | | final Void unused, final JsonPointer field, final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, LESS_THAN_OR_EQUAL_TO, null, valueAssertion); |
| | | context, resource, ROOT, field, LESS_THAN_OR_EQUAL_TO, null, valueAssertion); |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public Promise<Filter, ResourceException> visitPresentFilter( |
| | | final Void unused, final JsonPointer field) { |
| | | return propertyMapper.getLdapFilter(connection, resource, ROOT, field, PRESENT, null, null); |
| | | return propertyMapper.getLdapFilter(context, resource, ROOT, field, PRESENT, null, null); |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Filter, ResourceException> visitStartsWithFilter( |
| | | final Void unused, final JsonPointer field, final Object valueAssertion) { |
| | | return propertyMapper.getLdapFilter( |
| | | connection, resource, ROOT, field, STARTS_WITH, null, valueAssertion); |
| | | context, resource, ROOT, field, STARTS_WITH, null, valueAssertion); |
| | | } |
| | | }; |
| | | // Note that the returned LDAP filter may be null if it could not be mapped by any property mappers. |
| | |
| | | } |
| | | |
| | | private AsyncFunction<Filter, QueryResponse, ResourceException> runQuery( |
| | | final QueryRequest request, final QueryResourceHandler resourceHandler, final Connection connection) { |
| | | final Context context, final QueryRequest request, final QueryResourceHandler resourceHandler) { |
| | | return new AsyncFunction<Filter, QueryResponse, ResourceException>() { |
| | | // The following fields are guarded by sequenceLock. In addition, the sequenceLock ensures that |
| | | // we send one JSON resource at a time back to the client. |
| | |
| | | pageResultStartIndex = 0; |
| | | } |
| | | |
| | | connection.searchAsync(searchRequest, new SearchResultHandler() { |
| | | connectionFrom(context).searchAsync(searchRequest, new SearchResultHandler() { |
| | | @Override |
| | | public boolean handleEntry(final SearchResultEntry entry) { |
| | | // Search result entries will be returned before the search result/error so the only reason |
| | |
| | | final String revision = getRevisionFromEntry(entry); |
| | | final Resource subType = resource.resolveSubTypeFromObjectClasses(entry); |
| | | final PropertyMapper propertyMapper = subType.getPropertyMapper(); |
| | | propertyMapper.read(connection, subType, ROOT, entry) |
| | | propertyMapper.read(context, subType, ROOT, entry) |
| | | .thenOnResult(new ResultHandler<JsonValue>() { |
| | | @Override |
| | | public void handleResult(final JsonValue result) { |
| | |
| | | @Override |
| | | public Promise<ResourceResponse, ResourceException> apply(SearchResultEntry entry) { |
| | | final Resource subType = resource.resolveSubTypeFromObjectClasses(entry); |
| | | return encodeResourceResponse(connection, subType, entry); |
| | | return encodeResourceResponse(context, subType, entry); |
| | | } |
| | | }); |
| | | } |
| | |
| | | final Resource subType = resource.resolveSubTypeFromObjectClasses(entry); |
| | | subTypeHolder.set(subType); |
| | | final PropertyMapper propertyMapper = subType.getPropertyMapper(); |
| | | return propertyMapper.update(connection, subType , ROOT, entry, request.getContent()); |
| | | return propertyMapper.update(context, subType , ROOT, entry, request.getContent()); |
| | | } |
| | | }).thenAsync(new AsyncFunction<List<Modification>, ResourceResponse, ResourceException>() { |
| | | @Override |
| | |
| | | final Resource subType = subTypeHolder.get(); |
| | | if (modifications.isEmpty()) { |
| | | // No changes to be performed so just return the entry that we read. |
| | | return encodeResourceResponse(connection, subType, entryHolder.get()); |
| | | return encodeResourceResponse(context, subType, entryHolder.get()); |
| | | } |
| | | // Perform the modify operation. |
| | | final ModifyRequest modifyRequest = newModifyRequest(entryHolder.get().getName()); |
| | |
| | | addAssertionControl(modifyRequest, request.getRevision()); |
| | | modifyRequest.getModifications().addAll(modifications); |
| | | return connection.applyChangeAsync(modifyRequest) |
| | | .thenAsync(encodeUpdateResourceResponse(connection, subType), |
| | | .thenAsync(encodeUpdateResourceResponse(context, subType), |
| | | adaptLdapException(ResourceResponse.class)); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private Promise<ResourceResponse, ResourceException> encodeResourceResponse( |
| | | final Connection connection, final Resource resource, final Entry entry) { |
| | | final Context context, final Resource resource, final Entry entry) { |
| | | final PropertyMapper propertyMapper = resource.getPropertyMapper(); |
| | | return propertyMapper.read(connection, resource, ROOT, entry) |
| | | return propertyMapper.read(context, resource, ROOT, entry) |
| | | .then(new Function<JsonValue, ResourceResponse, ResourceException>() { |
| | | @Override |
| | | public ResourceResponse apply(final JsonValue value) { |
| | |
| | | } |
| | | |
| | | private AsyncFunction<Result, ResourceResponse, ResourceException> encodeUpdateResourceResponse( |
| | | final Connection connection, final Resource resource) { |
| | | final Context context, final Resource resource) { |
| | | return new AsyncFunction<Result, ResourceResponse, ResourceException>() { |
| | | @Override |
| | | public Promise<ResourceResponse, ResourceException> apply(Result result) { |
| | |
| | | final PostReadResponseControl postReadControl = |
| | | result.getControl(PostReadResponseControl.DECODER, decodeOptions); |
| | | if (postReadControl != null) { |
| | | return encodeResourceResponse(connection, resource, postReadControl.getEntry()); |
| | | return encodeResourceResponse(context, resource, postReadControl.getEntry()); |
| | | } |
| | | final PreReadResponseControl preReadControl = |
| | | result.getControl(PreReadResponseControl.DECODER, decodeOptions); |
| | | if (preReadControl != null) { |
| | | return encodeResourceResponse(connection, resource, preReadControl.getEntry()); |
| | | return encodeResourceResponse(context, resource, preReadControl.getEntry()); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | logger.error(ERR_DECODING_CONTROL.get(e.getLocalizedMessage()), e); |
| | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.GeneralizedTime; |
| | | import org.forgerock.opendj.ldap.LinkedAttribute; |
| | | import org.forgerock.opendj.ldap.schema.Syntax; |
| | | import org.forgerock.services.context.Context; |
| | | import org.forgerock.util.Function; |
| | | import org.forgerock.util.promise.NeverThrowsException; |
| | | |
| | |
| | | return new BadRequestException(message.toString(), cause); |
| | | } |
| | | |
| | | static Connection connectionFrom(final Context context) { |
| | | return context.asContext(AuthenticatedConnectionContext.class).getConnection(); |
| | | } |
| | | |
| | | /** Prevent instantiation. */ |
| | | private Utils() { |
| | | // No implementation required. |