/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* docs/licenses/cddl.txt
* or http://www.opensource.org/licenses/cddl1.php.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* docs/licenses/cddl.txt. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2021 Ping Identity Corporation
*/
package com.unboundid.directory.sdk.examples;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.unboundid.directory.sdk.common.types.LogSeverity;
import com.unboundid.directory.sdk.scim2.api.SCIMSubResourceTypeHandler;
import com.unboundid.directory.sdk.scim2.config.SCIMSubResourceTypeHandlerConfig;
import com.unboundid.directory.sdk.scim2.types.SCIMCreateRequest;
import com.unboundid.directory.sdk.scim2.types.SCIMDeleteRequest;
import com.unboundid.directory.sdk.scim2.types.SCIMLDAPAttributeMapper;
import com.unboundid.directory.sdk.scim2.types.SCIMLDAPInterface;
import com.unboundid.directory.sdk.scim2.types.SCIMModifyRequest;
import com.unboundid.directory.sdk.scim2.types.SCIMReplaceRequest;
import com.unboundid.directory.sdk.scim2.types.SCIMRetrieveRequest;
import com.unboundid.directory.sdk.scim2.types.SCIMSearchRequest;
import com.unboundid.directory.sdk.scim2.types.SCIMSearchResultListener;
import com.unboundid.directory.sdk.scim2.types.SCIMServerContext;
import com.unboundid.ldap.sdk.AddRequest;
import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.DeleteRequest;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModifyRequest;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchResultListener;
import com.unboundid.ldap.sdk.SearchResultReference;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.controls.PostReadRequestControl;
import com.unboundid.ldap.sdk.controls.PostReadResponseControl;
import com.unboundid.ldap.sdk.schema.Schema;
import com.unboundid.ldap.sdk.unboundidds.controls.NameWithEntryUUIDRequestControl;
import com.unboundid.scim2.common.GenericScimResource;
import com.unboundid.scim2.common.ScimResource;
import com.unboundid.scim2.common.exceptions.ResourceNotFoundException;
import com.unboundid.scim2.common.exceptions.ScimException;
import com.unboundid.scim2.common.exceptions.ServerErrorException;
import com.unboundid.scim2.common.messages.PatchRequest;
import com.unboundid.scim2.common.types.SchemaResource;
import com.unboundid.scim2.common.utils.JsonUtils;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.args.ArgumentParser;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
/**
* This is an example SCIM Sub-Resource Type Handler that demonstrates how to
* implement a <i>one-to-many</i> SCIM sub-resource type, which is a SCIM
* sub-resource type that may have zero to many sub-resource per parent SCIM
* resource.
* <p>
* Sub-resources provided by this example represent a user's "gadgets" and use
* the core SCIM schema "urn:pingidentity:schemas:2.0:GadgetExample". Each
* sub-resource is stored in the LDAP directory as a child entry of the LDAP
* entry representing the parent SCIM resource using the "device" object class.
* For example, given a SCIM resource backed by the LDAP entry
* "entryUUID=...,ou=people,dc=example,dc=com" (entryUUID omitted for brevity),
* a sub-resource would be stored in the LDAP entry
* "entryUUID=...,entryUUID=...,ou=people,dc=example,dc=com".
*
* This example SCIM Sub-Resource Type Handler requires that its SCIM schema
* be defined in the server configuration using the following commands:
* <pre>
* dsconfig create-scim-schema \
* --schema-name urn:pingidentity:schemas:2.0:GadgetExample \
* --set display-name:Gadget
* dsconfig create-scim-attribute \
* --schema-name urn:pingidentity:schemas:2.0:GadgetExample \
* --attribute-name name --set required:true
* dsconfig create-scim-attribute \
* --schema-name urn:pingidentity:schemas:2.0:GadgetExample \
* --attribute-name description
* dsconfig create-scim-attribute \
* --schema-name urn:pingidentity:schemas:2.0:GadgetExample \
* --attribute-name serialNumber
* </pre>
*/
public class ExampleOneToManySCIMSubResourceTypeHandler
extends SCIMSubResourceTypeHandler
{
/**
* The schema URN of this sub-resource type's core schema.
*/
public static final String SCIM_SCHEMA_ID =
"urn:pingidentity:schemas:2.0:GadgetExample";
/**
* The name of this sub-resource type's core schema.
*/
public static final String SCIM_SCHEMA_NAME = "Gadget";
// The LDAP attribute used for SCIM sub-resource IDs.
private static final String SUBRESOURCE_ID_ATTRIBUTE = "entryUUID";
// The LDAP object class used for SCIM sub-resource entries.
private static final String LDAP_OBJECT_CLASS = "device";
private static final ObjectMapper objectMapper =
JsonUtils.createObjectMapper();
private SCIMServerContext serverContext;
private SchemaResource coreSchema;
private String parentResourceType;
private SCIMLDAPInterface ldapInterface;
private SCIMLDAPAttributeMapper attributeMapper;
/**
* {@inheritDoc}
*/
@Override
public String getExtensionName()
{
return "Example One-To-Many SCIM Sub-Resource Type Handler";
}
/**
* {@inheritDoc}
*/
@Override
public String[] getExtensionDescription()
{
return new String[] {
"This is an example SCIM Sub-Resource Type Handler that exposes " +
"sub-resources representing a user's gadgets.",
"The attributes of each gadget sub-resource is stored as a child " +
"LDAP entry of the parent resource's entry using the 'device' " +
"object class."
};
}
/**
* Initializes this SCIM Sub-Resource Type Handler, loading the SCIM schema
* from the server configuration, setting up helper objects, and checking
* that the LDAP server supports certain required controls.
*
* @param serverContext A handle to the server context for the server in
* which this extension is running.
* @param config The configuration for this SCIM Sub-Resource Type
* Handler.
* @param parser The argument parser which has been initialized from
* the configuration for this SCIM Sub-Resource Type
* Handler.
* @throws Exception If a problem occurs while initializing this SCIM
* Sub-Resource Type Handler.
*/
@Override
public void initializeHandler(final SCIMServerContext serverContext,
final SCIMSubResourceTypeHandlerConfig config,
final ArgumentParser parser) throws Exception
{
this.serverContext = serverContext;
this.coreSchema = getSchemaFromConfiguration();
parentResourceType = config.getParentSCIMResourceTypeName();
try
{
ldapInterface = serverContext.getSCIMLDAPInterface(parentResourceType);
attributeMapper = serverContext.getLDAPAttributeMapper(
ldapInterface.getSchema(), objectMapper);
checkForRequiredControls();
}
catch (ScimException e)
{
String error = String.format("Initialization error: %s; Stack trace: %s",
e.getMessage(), StaticUtils.getStackTrace(e));
serverContext.logTraceMessage(LogSeverity.DEBUG, error);
throw new LDAPException(ResultCode.LOCAL_ERROR, e);
}
}
/**
* Gets the SCIM Sub-Resource Type's core schema.
*
* @return The core schema for the SCIM Sub-Resource Type.
*/
@Override
public SchemaResource getCoreSchema()
{
return coreSchema;
}
/**
* Indicates that this SCIM Sub-Resource Type Handler supports multiple
* sub-resources per parent resource.
*
* @return Always returns true to indicate that this SCIM Sub-Resource Type
* Handler supports multiple sub-resources per parent resource.
*/
@Override
public boolean supportsOneToMany()
{
// This handler supports multiple sub-resources per parent SCIM resource.
return true;
}
/**
* Handles SCIM create requests.
*
* @param request A SCIM create request.
* @return The created sub-resource.
* @throws ScimException If an error occurs.
*/
@Override
public ScimResource create(final SCIMCreateRequest request)
throws ScimException
{
// Get the parent LDAP entry's DN. This will be used as the base DN of the
// LDAP entry to create.
String parentDN =
getParentEntryDN(request.getHttpServletRequest(),
request.getResourceId());
// Use the SCIM request body to create a LDAP entry for the new
// sub-resource.
Entry entryToCreate =
scimResourceToLDAPEntry(attributeMapper, newEntryDN(parentDN),
request.getResourceToCreate());
Entry createdEntry =
addEntry(request.getHttpServletRequest(), entryToCreate);
// Convert the new entry to a ScimResource.
return ldapEntryToSCIMResource(attributeMapper, createdEntry);
}
/**
* Handles SCIM retrieve requests.
*
* @param request A SCIM retrieve request.
* @return The retrieved sub-resource.
* @throws ScimException If an error occurs.
*/
@Override
public ScimResource retrieve(final SCIMRetrieveRequest request)
throws ScimException
{
// Get the sub-resource's LDAP entry.
Entry entry =
getSubResourceEntry(request.getHttpServletRequest(),
request.getResourceId(), request.getSubResourceId());
// Convert the entry to a ScimResource.
return ldapEntryToSCIMResource(attributeMapper, entry);
}
/**
* Handles SCIM modify requests.
*
* @param request A SCIM modify request.
* @return The modified sub-resource.
* @throws ScimException If an error occurs.
*/
@Override
public ScimResource modify(final SCIMModifyRequest request)
throws ScimException
{
// Get the sub-resource's current LDAP entry.
Entry currentEntry =
getSubResourceEntry(request.getHttpServletRequest(),
request.getResourceId(), request.getSubResourceId());
// Convert the current entry to a ScimResource and apply the
// PatchRequest to it.
ScimResource currentSubResource =
ldapEntryToSCIMResource(attributeMapper, currentEntry);
PatchRequest patchRequest = request.getPatchRequest();
patchRequest.apply(currentSubResource.asGenericScimResource());
// Convert the modified ScimResource to an LDAP entry with the changes.
Entry updatedEntry =
scimResourceToLDAPEntry(attributeMapper, currentEntry.getDN(),
currentSubResource);
// Use the current entry and the updated entry to generate a set of
// modifications and apply them over LDAP, then convert the result to a
// ScimResource.
return update(request.getHttpServletRequest(), currentEntry, updatedEntry);
}
/**
* Handles SCIM replace requests.
*
* @param request A SCIM replace request.
* @return The replaced sub-resource.
* @throws ScimException If an error occurs.
*/
@Override
public ScimResource replace(final SCIMReplaceRequest request)
throws ScimException
{
// Get the sub-resource's current LDAP entry.
Entry currentEntry =
getSubResourceEntry(request.getHttpServletRequest(),
request.getResourceId(), request.getSubResourceId());
// Create an LDAP entry representation of the SCIM request body containing
// the changes.
Entry updatedEntry =
scimResourceToLDAPEntry(attributeMapper, currentEntry.getDN(),
request.getRequestResource());
// Use the current entry and the updated entry to generate a set of
// modifications and apply them over LDAP, then convert the result to a
// ScimResource.
return update(request.getHttpServletRequest(), currentEntry, updatedEntry);
}
/**
* Handles SCIM delete requests.
*
* @param request A SCIM delete request.
* @throws ScimException If an error occurs.
*/
@Override
public void delete(final SCIMDeleteRequest request) throws ScimException
{
// Get the sub-resource's current LDAP entry.
Entry entry =
getSubResourceEntry(request.getHttpServletRequest(),
request.getResourceId(), request.getSubResourceId());
// Delete the LDAP entry.
DeleteRequest deleteRequest = new DeleteRequest(entry.getDN());
ldapInterface.delete(request.getHttpServletRequest(), deleteRequest);
}
/**
* Handles SCIM search requests.
*
* @param request A SCIM search request.
* @throws ScimException If an error occurs.
*/
@Override
public void search(final SCIMSearchRequest request) throws ScimException
{
// Get the parent LDAP entry's DN. This will be used as the base DN for
// the sub-resource search.
String parentDN =
getParentEntryDN(request.getHttpServletRequest(),
request.getResourceId());
// Create a SCIMSearchResultListenerAdapter to wrap the
// SCIMSearchResultListener provided by the server. This will return
// results back to the server as they are received over the LDAP interface.
SCIMSearchResultListenerAdapter ldapSearchResultListener =
new SCIMSearchResultListenerAdapter(ldapInterface.getSchema(),
objectMapper, serverContext, request.getSearchResultListener());
// Provide an LDAP filter that simply returns all child entries with the
// "device" object class rather than attempting to map the SCIM filter from
// the SCIMSearchRequest to an LDAP filter. When result sets are expected
// to be small, this is acceptable, because the server will apply the SCIM
// filter to the SCIMSearchResultListener's result set.
Filter filter =
Filter.createEqualityFilter("objectClass", LDAP_OBJECT_CLASS);
SearchRequest searchRequest =
new SearchRequest(ldapSearchResultListener, parentDN,
SearchScope.SUB, filter, "*", "+");
ldapInterface.search(request.getHttpServletRequest(), searchRequest);
}
/**
* Throws an exception if the LDAP server backing this SCIM Sub-Resource Type
* does not support the LDAP controls used by this handler.
*
* @throws ScimException If the LDAP server does not support the LDAP
* controls used by this handler.
*/
private void checkForRequiredControls() throws ScimException
{
Set<String> missingRequiredControls = new LinkedHashSet<>();
if (!ldapInterface.getSupportedControls().contains(
PostReadRequestControl.POST_READ_REQUEST_OID))
{
missingRequiredControls.add("Post-Read Request Control");
}
if (!ldapInterface.getSupportedControls().contains(
NameWithEntryUUIDRequestControl.NAME_WITH_ENTRY_UUID_REQUEST_OID))
{
missingRequiredControls.add("Name with entryUUID Request Control");
}
if (!missingRequiredControls.isEmpty())
{
String errorPrefix = "Cannot initialize extension because the LDAP " +
"server does not support the following control(s): ";
StringJoiner joiner = new StringJoiner(",", errorPrefix, "");
for (String control : missingRequiredControls)
{
joiner.add(control);
}
throw new ServerErrorException(joiner.toString());
}
}
/**
* Retrieves this handler's SCIM schema from the server configuration.
*
* @return The SCIM schema used by this handler.
* @throws LDAPException If the SCIM schema does not exist.
*/
private SchemaResource getSchemaFromConfiguration() throws LDAPException
{
Optional<SchemaResource> configuredSchema =
serverContext.getSCIMSchemas()
.stream()
.filter(schema -> schema.getId().equals(SCIM_SCHEMA_ID))
.findFirst();
return configuredSchema.orElseThrow(
() -> new LDAPException(ResultCode.LOCAL_ERROR,
String.format("Could not find required SCIM schema '%s'",
SCIM_SCHEMA_ID)));
}
/**
* Retrieves the DN of the LDAP entry representing a SCIM parent resource.
*
* @param httpServletRequest The HTTP servlet request associated with the
* current operation.
* @param scimResourceID The SCIM resource ID.
* This must not be null.
*
* @return The SCIM parent resource's LDAP entry DN.
* @throws ScimException If the entry cannot be retrieved.
*/
private String getParentEntryDN(final HttpServletRequest httpServletRequest,
final String scimResourceID)
throws ScimException
{
Filter filter = Filter.createEqualityFilter(
serverContext.getIDAttribute(parentResourceType), scimResourceID);
SearchRequest searchRequest =
new SearchRequest(ldapInterface.getBaseDN(), SearchScope.SUB,
filter, "1.1");
Entry entry =
ldapInterface.searchForEntry(httpServletRequest, searchRequest);
return entry.getDN();
}
/**
* Retrieves the LDAP entry representing a SCIM sub-resource.
*
* @param httpServletRequest The HTTP servlet request associated with the
* current operation.
* @param resourceId The parent SCIM resource ID.
* @param subResourceId The SCIM sub-resource ID.
* @return The SCIM sub-resource's LDAP entry.
* @throws ScimException If the entry cannot be retrieved.
*/
private Entry getSubResourceEntry(final HttpServletRequest httpServletRequest,
final String resourceId,
final String subResourceId)
throws ScimException
{
String parentDN = getParentEntryDN(httpServletRequest, resourceId);
Filter filter =
Filter.createEqualityFilter(SUBRESOURCE_ID_ATTRIBUTE, subResourceId);
SearchRequest searchRequest =
new SearchRequest(parentDN, SearchScope.SUB, filter, "*", "+");
SearchResult result =
ldapInterface.search(httpServletRequest, searchRequest);
if (result.getEntryCount() == 0)
{
throw new ResourceNotFoundException(
String.format("SCIM sub-resource %s not found", subResourceId));
}
return result.getSearchEntries().get(0);
}
/**
* Gets a DN to be used for creating a new SCIM sub-resource's LDAP entry.
*
* @param parentDN The parent SCIM resource's LDAP DN.
*
* @return The new DN.
*/
private String newEntryDN(final String parentDN)
{
// Use a placeholder entryUUID value. When adding an entry, the Name with
// EntryUUID request control will cause the LDAP server to generate a
// unique value.
return "entryUUID=00000000-0000-0000-0000-000000000000," + parentDN;
}
/**
* Adds a new entry via LDAP.
*
* @param httpServletRequest The HTTP servlet request associated with the
* current operation.
* @param entryToAdd The LDAP entry to add. This entry is expected
* to use entryUUID as its RDN attribute. Use
* {@link #newEntryDN(String)} to create an
* appropriate DN for the entry to be added.
*
* @return The newly created LDAP entry.
* @throws ScimException If the entry cannot be added.
*/
private Entry addEntry(final HttpServletRequest httpServletRequest,
final Entry entryToAdd)
throws ScimException
{
AddRequest addRequest = new AddRequest(entryToAdd);
addRequest.addControl(new NameWithEntryUUIDRequestControl());
addRequest.addControl(new PostReadRequestControl("*", "+"));
LDAPResult result = ldapInterface.add(httpServletRequest, addRequest);
PostReadResponseControl postReadResponseControl =
getPostReadResponseControl(result);
return postReadResponseControl.getEntry();
}
/**
* Updates a SCIM sub-resource using two LDAP representations of the
* sub-resource: The LDAP entry without changes, and the LDAP entry with
* changes.
*
* @param httpServletRequest The HTTP servlet request associated with the
* current operation.
* @param currentEntry The current LDAP entry.
* @param updatedEntry The same LDAP entry with changes.
*
* @return The SCIM resource after applying changes. If no
* modifications needed to be made, then an
* unchanged resource will be returned.
* @throws ScimException If the update fails.
*/
private ScimResource update(final HttpServletRequest httpServletRequest,
final Entry currentEntry,
final Entry updatedEntry)
throws ScimException
{
// Generate a list of LDAP modifications.
List<Modification> ldapMods =
Entry.diff(currentEntry, updatedEntry, true,
"cn", "description", "serialNumber");
// If there are modifications to make, continue and perform and LDAP modify.
if (!ldapMods.isEmpty())
{
ModifyRequest modifyRequest =
new ModifyRequest(currentEntry.getDN(), ldapMods);
modifyRequest.addControl(new PostReadRequestControl("*", "+"));
LDAPResult result =
ldapInterface.modify(httpServletRequest, modifyRequest);
// Use the Post-Read Response Control to get the updated LDAP entry.
PostReadResponseControl postReadResponseControl =
getPostReadResponseControl(result);
return ldapEntryToSCIMResource(attributeMapper,
postReadResponseControl.getEntry());
}
// If there were no modifications to make, return the unchanged resource.
return ldapEntryToSCIMResource(attributeMapper, currentEntry);
}
/**
* Extracts the Post-Read Response Control from an LDAP result.
*
* @param result An LDAP result containing a Post-Read Response
* Control.
*
* @return The Post-Read Response Control.
* @throws ScimException If a Post-Read Response Control cannot be obtained.
*/
private PostReadResponseControl getPostReadResponseControl(
final LDAPResult result) throws ScimException
{
Control control =
result.getResponseControl(PostReadRequestControl.POST_READ_REQUEST_OID);
try
{
if (control == null)
{
throw new LDAPException(ResultCode.CONTROL_NOT_FOUND,
"LDAP result did not include Post-Read Response Control");
}
if (control instanceof PostReadResponseControl)
{
return (PostReadResponseControl) control;
}
return new PostReadResponseControl(control.getOID(), control.isCritical(),
control.getValue());
}
catch (LDAPException e)
{
String error =
String.format("Error reading Post-Read Response Control " +
"from LDAP result: %s; Stack trace: %s",
e.getExceptionMessage(), StaticUtils.getStackTrace(e));
serverContext.logTraceMessage(LogSeverity.SEVERE_ERROR, error);
throw new ServerErrorException(error, null, e);
}
}
/**
* Converts a SCIM gadget sub-resource object to an LDAP device entry.
*
* @param attributeMapper An SCIMLDAPAttributeMapper instance.
* @param dn The DN to use for the LDAP entry.
* @param resource The SCIM resource.
*
* @return The LDAP entry.
* @throws ScimException If an attribute value cannot be converted.
*/
private static Entry scimResourceToLDAPEntry(
final SCIMLDAPAttributeMapper attributeMapper,
final String dn,
final ScimResource resource)
throws ScimException
{
ObjectNode fields = resource.asGenericScimResource().getObjectNode();
Entry entry = new Entry(dn);
entry.setAttribute("objectClass", "top", LDAP_OBJECT_CLASS);
entry.setAttribute(attributeMapper.toLdapAttribute(
fields.path("name"), "name", "cn"));
if (!fields.path("description").isMissingNode())
{
entry.setAttribute(attributeMapper.toLdapAttribute(
fields.path("description"), "description", "description"));
}
if (!fields.path("serialNumber").isMissingNode())
{
entry.setAttribute(attributeMapper.toLdapAttribute(
fields.path("serialNumber"), "serialNumber", "serialNumber"));
}
return entry;
}
/**
* Converts an LDAP device entry to a SCIM gadget sub-resource object.
*
* @param attributeMapper An SCIMLDAPAttributeMapper instance.
* @param entry The LDAP entry.
*
* @return The SCIM resource.
* @throws ScimException If an attribute value cannot be converted.
*/
private static ScimResource ldapEntryToSCIMResource(
final SCIMLDAPAttributeMapper attributeMapper,
final Entry entry)
throws ScimException
{
ObjectNode objectNode = JsonUtils.getJsonNodeFactory().objectNode();
TextNode name =
attributeMapper.toTextNode(entry.getAttribute("cn"));
objectNode.replace("name", name);
if (entry.getAttribute("description") != null)
{
TextNode description =
attributeMapper.toTextNode(entry.getAttribute("description"));
objectNode.replace("description", description);
}
if (entry.getAttribute("serialNumber") != null)
{
TextNode serialNumber =
attributeMapper.toTextNode(entry.getAttribute("serialNumber"));
objectNode.replace("serialNumber", serialNumber);
}
GenericScimResource resource = new GenericScimResource(objectNode);
resource.setSchemaUrns(Collections.singletonList(SCIM_SCHEMA_ID));
if (entry.getAttributeValue(SUBRESOURCE_ID_ATTRIBUTE) != null)
{
resource.setId(entry.getAttributeValue(SUBRESOURCE_ID_ATTRIBUTE));
}
return resource;
}
/**
* An LDAP search result listener implementation that maps its search results
* to a {@link SCIMSearchResultListener}.
*/
private static class SCIMSearchResultListenerAdapter
implements SearchResultListener
{
private static final long serialVersionUID = 3211616264384619807L;
private final SCIMServerContext serverContext;
private final SCIMLDAPAttributeMapper attributeMapper;
private final SCIMSearchResultListener scimSearchResultListener;
/**
* Constructs a SCIMSearchResultListenerAdapter.
*
* @param ldapSchema The LDAP server schema.
* @param objectMapper A Jackson object mapper.
* @param serverContext The extension's server context.
* @param scimSearchResultListener The SCIM search result listener
* associated with the current operation.
*/
SCIMSearchResultListenerAdapter(
final Schema ldapSchema,
final ObjectMapper objectMapper,
final SCIMServerContext serverContext,
final SCIMSearchResultListener scimSearchResultListener)
{
this.serverContext = serverContext;
this.attributeMapper =
serverContext.getLDAPAttributeMapper(ldapSchema, objectMapper);
this.scimSearchResultListener = scimSearchResultListener;
}
/**
* {@inheritDoc}
*/
@Override
public void searchEntryReturned(final SearchResultEntry searchResultEntry)
{
try
{
scimSearchResultListener.searchResultReturned(
ldapEntryToSCIMResource(attributeMapper, searchResultEntry));
}
catch (ScimException e)
{
serverContext.logTraceMessage(LogSeverity.SEVERE_ERROR,
String.format("Unable to map search result entry '%s' " +
"to a ScimResource", searchResultEntry.getDN()));
}
}
/**
* {@inheritDoc}
*/
@Override
public void searchReferenceReturned(
final SearchResultReference searchResultReference)
{
// No implementation needed.
}
}
}