| | |
| | | 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); |