use of com.unboundid.ldap.protocol.LDAPMessage in project ldapsdk by pingidentity.
the class InMemoryRequestHandler method processSearchRequest.
/**
* Attempts to process the provided search request. The attempt will fail
* if any of the following conditions is true:
* <UL>
* <LI>There is a problem with any of the request controls.</LI>
* <LI>The modify DN request contains a malformed target DN, new RDN, or
* new superior DN.</LI>
* <LI>The new DN of the entry would conflict with the DN of an existing
* entry.</LI>
* <LI>The new DN of the entry would exist outside the set of defined
* base DNs.</LI>
* <LI>The new DN of the entry is not a defined base DN and does not exist
* immediately below an existing entry.</LI>
* </UL>
*
* @param messageID The message ID of the LDAP message containing the
* search request.
* @param request The search request that was included in the LDAP
* message that was received.
* @param controls The set of controls included in the LDAP message.
* It may be empty if there were no controls, but will
* not be {@code null}.
* @param entryList A list to which to add search result entries
* intended for return to the client. It must not be
* {@code null}.
* @param referenceList A list to which to add search result references
* intended for return to the client. It must not be
* {@code null}.
*
* @return The {@link LDAPMessage} containing the response to send to the
* client. The protocol op in the {@code LDAPMessage} must be an
* {@code SearchResultDoneProtocolOp}.
*/
@NotNull()
LDAPMessage processSearchRequest(final int messageID, @NotNull final SearchRequestProtocolOp request, @NotNull final List<Control> controls, @NotNull final List<SearchResultEntry> entryList, @NotNull final List<SearchResultReference> referenceList) {
synchronized (entryMap) {
// Sleep before processing, if appropriate.
final long processingStartTime = System.currentTimeMillis();
sleepBeforeProcessing();
// Look at the filter and see if it contains any unsupported elements.
try {
ensureFilterSupported(request.getFilter());
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(le.getResultCode().intValue(), null, le.getMessage(), null));
}
// Look at the time limit for the search request and see if sleeping
// would have caused us to exceed that time limit. It's extremely
// unlikely that any search in the in-memory directory server would take
// a second or more to complete, and that's the minimum time limit that
// can be requested, so there's no need to check the time limit in most
// cases. However, someone may want to force a "time limit exceeded"
// response by configuring a delay that is greater than the requested time
// limit, so we should check now to see if that's been exceeded.
final long timeLimitMillis = 1000L * request.getTimeLimit();
if (timeLimitMillis > 0L) {
final long timeLimitExpirationTime = processingStartTime + timeLimitMillis;
if (System.currentTimeMillis() >= timeLimitExpirationTime) {
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.TIME_LIMIT_EXCEEDED_INT_VALUE, null, ERR_MEM_HANDLER_TIME_LIMIT_EXCEEDED.get(), null));
}
}
// Process the provided request controls.
final Map<String, Control> controlMap;
try {
controlMap = RequestControlPreProcessor.processControls(LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST, controls);
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(le.getResultCode().intValue(), null, le.getMessage(), null));
}
final ArrayList<Control> responseControls = new ArrayList<>(1);
// If this operation type is not allowed, then reject it.
final boolean isInternalOp = controlMap.containsKey(OID_INTERNAL_OPERATION_REQUEST_CONTROL);
if ((!isInternalOp) && (!config.getAllowedOperationTypes().contains(OperationType.SEARCH))) {
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.UNWILLING_TO_PERFORM_INT_VALUE, null, ERR_MEM_HANDLER_SEARCH_NOT_ALLOWED.get(), null));
}
// client is authenticated.
if ((authenticatedDN.isNullDN() && config.getAuthenticationRequiredOperationTypes().contains(OperationType.SEARCH))) {
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.INSUFFICIENT_ACCESS_RIGHTS_INT_VALUE, null, ERR_MEM_HANDLER_SEARCH_REQUIRES_AUTH.get(), null));
}
// Get the parsed base DN.
final DN baseDN;
final Schema schema = schemaRef.get();
try {
baseDN = new DN(request.getBaseDN(), schema);
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.INVALID_DN_SYNTAX_INT_VALUE, null, ERR_MEM_HANDLER_SEARCH_MALFORMED_BASE.get(request.getBaseDN(), le.getMessage()), null));
}
// See if the search base or one of its superiors is a smart referral.
final boolean hasManageDsaIT = controlMap.containsKey(ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID);
if (!hasManageDsaIT) {
final Entry referralEntry = findNearestReferral(baseDN);
if (referralEntry != null) {
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.REFERRAL_INT_VALUE, referralEntry.getDN(), INFO_MEM_HANDLER_REFERRAL_ENCOUNTERED.get(), getReferralURLs(baseDN, referralEntry)));
}
}
// Make sure that the base entry exists. It may be the root DSE or
// subschema subentry.
final Entry baseEntry;
boolean includeChangeLog = true;
if (baseDN.isNullDN()) {
baseEntry = generateRootDSE();
includeChangeLog = false;
} else if (baseDN.equals(subschemaSubentryDN)) {
baseEntry = subschemaSubentryRef.get();
} else {
baseEntry = entryMap.get(baseDN);
}
if (baseEntry == null) {
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.NO_SUCH_OBJECT_INT_VALUE, getMatchedDNString(baseDN), ERR_MEM_HANDLER_SEARCH_BASE_DOES_NOT_EXIST.get(request.getBaseDN()), null));
}
// controls.
try {
handleAssertionRequestControl(controlMap, baseEntry);
handleProxiedAuthControl(controlMap);
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(le.getResultCode().intValue(), null, le.getMessage(), null));
}
// Determine whether to include subentries in search results.
final boolean includeSubEntries;
final boolean includeNonSubEntries;
final SearchScope scope = request.getScope();
if (scope == SearchScope.BASE) {
includeSubEntries = true;
includeNonSubEntries = true;
} else if (controlMap.containsKey(DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID)) {
includeSubEntries = true;
includeNonSubEntries = false;
} else if (controlMap.containsKey(RFC3672SubentriesRequestControl.SUBENTRIES_REQUEST_OID)) {
includeSubEntries = true;
final RFC3672SubentriesRequestControl c = (RFC3672SubentriesRequestControl) controlMap.get(RFC3672SubentriesRequestControl.SUBENTRIES_REQUEST_OID);
includeNonSubEntries = (!c.returnOnlySubEntries());
} else if (baseEntry.hasObjectClass("ldapSubEntry") || baseEntry.hasObjectClass("inheritableLDAPSubEntry")) {
includeSubEntries = true;
includeNonSubEntries = true;
} else if (filterIncludesLDAPSubEntry(request.getFilter())) {
includeSubEntries = true;
includeNonSubEntries = true;
} else {
includeSubEntries = false;
includeNonSubEntries = true;
}
// Create a temporary list to hold all of the entries to be returned.
// These entries will not have been pared down based on the requested
// attributes.
final List<Entry> fullEntryList = new ArrayList<>(entryMap.size());
findEntriesAndRefs: {
// Check the scope. If it is a base-level search, then we only need to
// examine the base entry. Otherwise, we'll have to scan the entire
// entry map.
final Filter filter = request.getFilter();
if (scope == SearchScope.BASE) {
try {
if (filter.matchesEntry(baseEntry, schema)) {
processSearchEntry(baseEntry, includeSubEntries, includeNonSubEntries, includeChangeLog, hasManageDsaIT, fullEntryList, referenceList);
}
} catch (final Exception e) {
Debug.debugException(e);
}
break findEntriesAndRefs;
}
// set.
if ((scope == SearchScope.ONE) && baseDN.isNullDN()) {
for (final DN dn : baseDNs) {
final Entry e = entryMap.get(dn);
if (e != null) {
try {
if (filter.matchesEntry(e, schema)) {
processSearchEntry(e, includeSubEntries, includeNonSubEntries, includeChangeLog, hasManageDsaIT, fullEntryList, referenceList);
}
} catch (final Exception ex) {
Debug.debugException(ex);
}
}
}
break findEntriesAndRefs;
}
// Try to use indexes to process the request. If we can't use any
// indexes to get a candidate list, then just iterate over all the
// entries. It's not necessary to consider the root DSE for non-base
// scopes.
final Set<DN> candidateDNs = indexSearch(filter);
if (candidateDNs == null) {
for (final Map.Entry<DN, ReadOnlyEntry> me : entryMap.entrySet()) {
final DN dn = me.getKey();
final Entry entry = me.getValue();
try {
if (dn.matchesBaseAndScope(baseDN, scope)) {
if (filter.matchesEntry(entry, schema) || (((!hasManageDsaIT) && entry.hasObjectClass("referral") && entry.hasAttribute("ref")))) {
processSearchEntry(entry, includeSubEntries, includeNonSubEntries, includeChangeLog, hasManageDsaIT, fullEntryList, referenceList);
}
}
} catch (final Exception e) {
Debug.debugException(e);
}
}
} else {
for (final DN dn : candidateDNs) {
try {
if (!dn.matchesBaseAndScope(baseDN, scope)) {
continue;
}
final Entry entry = entryMap.get(dn);
if (filter.matchesEntry(entry, schema) || (((!hasManageDsaIT) && entry.hasObjectClass("referral") && entry.hasAttribute("ref")))) {
processSearchEntry(entry, includeSubEntries, includeNonSubEntries, includeChangeLog, hasManageDsaIT, fullEntryList, referenceList);
}
} catch (final Exception e) {
Debug.debugException(e);
}
}
}
}
// If the request included the server-side sort request control, then sort
// the matching entries appropriately.
final ServerSideSortRequestControl sortRequestControl = (ServerSideSortRequestControl) controlMap.get(ServerSideSortRequestControl.SERVER_SIDE_SORT_REQUEST_OID);
if (sortRequestControl != null) {
final EntrySorter entrySorter = new EntrySorter(false, schema, sortRequestControl.getSortKeys());
final SortedSet<Entry> sortedEntrySet = entrySorter.sort(fullEntryList);
fullEntryList.clear();
fullEntryList.addAll(sortedEntrySet);
responseControls.add(new ServerSideSortResponseControl(ResultCode.SUCCESS, null));
}
// If the request included the simple paged results control, then handle
// it.
final SimplePagedResultsControl pagedResultsControl = (SimplePagedResultsControl) controlMap.get(SimplePagedResultsControl.PAGED_RESULTS_OID);
if (pagedResultsControl != null) {
final int totalSize = fullEntryList.size();
final int pageSize = pagedResultsControl.getSize();
final ASN1OctetString cookie = pagedResultsControl.getCookie();
final int offset;
if ((cookie == null) || (cookie.getValueLength() == 0)) {
// This is the first request in the series, so start at the beginning
// of the list.
offset = 0;
} else {
// offset within the result list at which to start the next batch.
try {
final ASN1Integer offsetInteger = ASN1Integer.decodeAsInteger(cookie.getValue());
offset = offsetInteger.intValue();
} catch (final Exception e) {
Debug.debugException(e);
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.PROTOCOL_ERROR_INT_VALUE, null, ERR_MEM_HANDLER_MALFORMED_PAGED_RESULTS_COOKIE.get(), null), responseControls);
}
}
// Create an iterator that will be used to remove entries from the
// result set that are outside of the requested page of results.
int pos = 0;
final Iterator<Entry> iterator = fullEntryList.iterator();
// offset.
while (iterator.hasNext() && (pos < offset)) {
iterator.next();
iterator.remove();
pos++;
}
// Next, skip over the entries that should be returned.
int keptEntries = 0;
while (iterator.hasNext() && (keptEntries < pageSize)) {
iterator.next();
pos++;
keptEntries++;
}
// to include in the response. Otherwise, use an empty cookie.
if (iterator.hasNext()) {
responseControls.add(new SimplePagedResultsControl(totalSize, new ASN1OctetString(new ASN1Integer(pos).encode()), false));
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
} else {
responseControls.add(new SimplePagedResultsControl(totalSize, new ASN1OctetString(), false));
}
}
// If the request includes the virtual list view request control, then
// handle it.
final VirtualListViewRequestControl vlvRequest = (VirtualListViewRequestControl) controlMap.get(VirtualListViewRequestControl.VIRTUAL_LIST_VIEW_REQUEST_OID);
if (vlvRequest != null) {
final int totalEntries = fullEntryList.size();
final ASN1OctetString assertionValue = vlvRequest.getAssertionValue();
// Figure out the position of the target entry in the list.
int offset = vlvRequest.getTargetOffset();
if (assertionValue == null) {
// The offset is one-based, so we need to adjust it for the list's
// zero-based offset. Also, make sure to put it within the bounds of
// the list.
offset--;
offset = Math.max(0, offset);
offset = Math.min(fullEntryList.size(), offset);
} else {
final SortKey primarySortKey = sortRequestControl.getSortKeys()[0];
final Entry testEntry = new Entry("cn=test", schema, new Attribute(primarySortKey.getAttributeName(), assertionValue));
final EntrySorter entrySorter = new EntrySorter(false, schema, primarySortKey);
offset = fullEntryList.size();
for (int i = 0; i < fullEntryList.size(); i++) {
if (entrySorter.compare(fullEntryList.get(i), testEntry) >= 0) {
offset = i;
break;
}
}
}
// Get the start and end positions based on the before and after counts.
final int beforeCount = Math.max(0, vlvRequest.getBeforeCount());
final int afterCount = Math.max(0, vlvRequest.getAfterCount());
final int start = Math.max(0, (offset - beforeCount));
final int end = Math.min(fullEntryList.size(), (offset + afterCount + 1));
// Create an iterator to use to alter the list so that it only contains
// the appropriate set of entries.
int pos = 0;
final Iterator<Entry> iterator = fullEntryList.iterator();
while (iterator.hasNext()) {
iterator.next();
if ((pos < start) || (pos >= end)) {
iterator.remove();
}
pos++;
}
// Create the appropriate response control.
responseControls.add(new VirtualListViewResponseControl((offset + 1), totalEntries, ResultCode.SUCCESS, null));
}
// Process the set of requested attributes so that we can pare down the
// entries.
final SearchEntryParer parer = new SearchEntryParer(request.getAttributes(), schema);
final int sizeLimit;
if (request.getSizeLimit() > 0) {
sizeLimit = Math.min(request.getSizeLimit(), maxSizeLimit);
} else {
sizeLimit = maxSizeLimit;
}
int entryCount = 0;
for (final Entry e : fullEntryList) {
entryCount++;
if (entryCount > sizeLimit) {
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.SIZE_LIMIT_EXCEEDED_INT_VALUE, null, ERR_MEM_HANDLER_SEARCH_SIZE_LIMIT_EXCEEDED.get(), null), responseControls);
}
final Entry trimmedEntry = parer.pareEntry(e);
if (request.typesOnly()) {
final Entry typesOnlyEntry = new Entry(trimmedEntry.getDN(), schema);
for (final Attribute a : trimmedEntry.getAttributes()) {
typesOnlyEntry.addAttribute(new Attribute(a.getName()));
}
entryList.add(new SearchResultEntry(typesOnlyEntry));
} else {
entryList.add(new SearchResultEntry(trimmedEntry));
}
}
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, null, null), responseControls);
}
}
use of com.unboundid.ldap.protocol.LDAPMessage in project ldapsdk by pingidentity.
the class InMemoryRequestHandler method addEntry.
/**
* Attempts to add the provided entry to the in-memory data set. The attempt
* will fail if any of the following conditions is true:
* <UL>
* <LI>The provided entry has a malformed DN.</LI>
* <LI>The provided entry has the null DN.</LI>
* <LI>The provided entry has a DN that is the same as or subordinate to the
* subschema subentry.</LI>
* <LI>An entry already exists with the same DN as the entry in the provided
* request.</LI>
* <LI>The entry is outside the set of base DNs for the server.</LI>
* <LI>The entry is below one of the defined base DNs but the immediate
* parent entry does not exist.</LI>
* <LI>If a schema was provided, and the entry is not valid according to the
* constraints of that schema.</LI>
* </UL>
*
* @param entry The entry to be added. It must not be
* {@code null}.
* @param ignoreNoUserModification Indicates whether to ignore constraints
* normally imposed by the
* NO-USER-MODIFICATION element in attribute
* type definitions.
*
* @throws LDAPException If a problem occurs while attempting to add the
* provided entry.
*/
public void addEntry(@NotNull final Entry entry, final boolean ignoreNoUserModification) throws LDAPException {
final List<Control> controls;
if (ignoreNoUserModification) {
controls = new ArrayList<>(1);
controls.add(new Control(OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
} else {
controls = Collections.emptyList();
}
final AddRequestProtocolOp addRequest = new AddRequestProtocolOp(entry.getDN(), new ArrayList<>(entry.getAttributes()));
final LDAPMessage resultMessage = processAddRequest(-1, addRequest, controls);
final AddResponseProtocolOp addResponse = resultMessage.getAddResponseProtocolOp();
if (addResponse.getResultCode() != ResultCode.SUCCESS_INT_VALUE) {
throw new LDAPException(ResultCode.valueOf(addResponse.getResultCode()), addResponse.getDiagnosticMessage(), addResponse.getMatchedDN(), stringListToArray(addResponse.getReferralURLs()));
}
}
use of com.unboundid.ldap.protocol.LDAPMessage in project ldapsdk by pingidentity.
the class InMemoryRequestHandler method processBindRequest.
/**
* Attempts to process the provided bind request. The attempt will fail if
* any of the following conditions is true:
* <UL>
* <LI>There is a problem with any of the request controls.</LI>
* <LI>The bind request is for a SASL bind for which no SASL mechanism
* handler is defined.</LI>
* <LI>The bind request contains a malformed bind DN.</LI>
* <LI>The bind DN is not the null DN and is not the DN of any entry in the
* data set.</LI>
* <LI>The bind password is empty and the bind DN is not the null DN.</LI>
* <LI>The target user does not have any password value that matches the
* provided bind password.</LI>
* </UL>
*
* @param messageID The message ID of the LDAP message containing the bind
* request.
* @param request The bind request that was included in the LDAP message
* that was received.
* @param controls The set of controls included in the LDAP message. It
* may be empty if there were no controls, but will not be
* {@code null}.
*
* @return The {@link LDAPMessage} containing the response to send to the
* client. The protocol op in the {@code LDAPMessage} must be a
* {@code BindResponseProtocolOp}.
*/
@Override()
@NotNull()
public LDAPMessage processBindRequest(final int messageID, @NotNull final BindRequestProtocolOp request, @NotNull final List<Control> controls) {
synchronized (entryMap) {
// Sleep before processing, if appropriate.
sleepBeforeProcessing();
// If this operation type is not allowed, then reject it.
if (!config.getAllowedOperationTypes().contains(OperationType.BIND)) {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.UNWILLING_TO_PERFORM_INT_VALUE, null, ERR_MEM_HANDLER_BIND_NOT_ALLOWED.get(), null, null));
}
authenticatedDN = DN.NULL_DN;
// request, then ensure that the request includes credentials.
if ((authenticatedDN.isNullDN() && config.getAuthenticationRequiredOperationTypes().contains(OperationType.BIND))) {
if ((request.getCredentialsType() == BindRequestProtocolOp.CRED_TYPE_SIMPLE) && ((request.getSimplePassword() == null) || request.getSimplePassword().getValueLength() == 0)) {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.INVALID_CREDENTIALS_INT_VALUE, null, ERR_MEM_HANDLER_BIND_REQUIRES_AUTH.get(), null, null));
}
}
// Get the parsed bind DN.
final DN bindDN;
try {
bindDN = new DN(request.getBindDN(), schemaRef.get());
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.INVALID_DN_SYNTAX_INT_VALUE, null, ERR_MEM_HANDLER_BIND_MALFORMED_DN.get(request.getBindDN(), le.getMessage()), null, null));
}
// mechanism handler that can be used to process it.
if (request.getCredentialsType() == BindRequestProtocolOp.CRED_TYPE_SASL) {
final String mechanism = request.getSASLMechanism();
final InMemorySASLBindHandler handler = saslBindHandlers.get(mechanism);
if (handler == null) {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.AUTH_METHOD_NOT_SUPPORTED_INT_VALUE, null, ERR_MEM_HANDLER_SASL_MECH_NOT_SUPPORTED.get(mechanism), null, null));
}
try {
final BindResult bindResult = handler.processSASLBind(this, messageID, bindDN, request.getSASLCredentials(), controls);
// unauthenticated, then see if we allow that.
if ((bindResult.getResultCode() == ResultCode.SUCCESS) && (authenticatedDN == DN.NULL_DN) && config.getAuthenticationRequiredOperationTypes().contains(OperationType.BIND)) {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.INVALID_CREDENTIALS_INT_VALUE, null, ERR_MEM_HANDLER_BIND_REQUIRES_AUTH.get(), null, null));
}
return new LDAPMessage(messageID, new BindResponseProtocolOp(bindResult.getResultCode().intValue(), bindResult.getMatchedDN(), bindResult.getDiagnosticMessage(), Arrays.asList(bindResult.getReferralURLs()), bindResult.getServerSASLCredentials()), Arrays.asList(bindResult.getResponseControls()));
} catch (final Exception e) {
Debug.debugException(e);
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.OTHER_INT_VALUE, null, ERR_MEM_HANDLER_SASL_BIND_FAILURE.get(StaticUtils.getExceptionMessage(e)), null, null));
}
}
// If we've gotten here, then the bind must use simple authentication.
// Process the provided request controls.
final Map<String, Control> controlMap;
try {
controlMap = RequestControlPreProcessor.processControls(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST, controls);
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new BindResponseProtocolOp(le.getResultCode().intValue(), null, le.getMessage(), null, null));
}
final ArrayList<Control> responseControls = new ArrayList<>(1);
// If the bind DN is the null DN, then the bind will be considered
// successful as long as the password is also empty.
final ASN1OctetString bindPassword = request.getSimplePassword();
if (bindDN.isNullDN()) {
if (bindPassword.getValueLength() == 0) {
if (controlMap.containsKey(AuthorizationIdentityRequestControl.AUTHORIZATION_IDENTITY_REQUEST_OID)) {
responseControls.add(new AuthorizationIdentityResponseControl(""));
}
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, null, null, null), responseControls);
} else {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.INVALID_CREDENTIALS_INT_VALUE, null, ERR_MEM_HANDLER_BIND_WRONG_PASSWORD.get(request.getBindDN()), null, null));
}
}
// request.
if ((!bindDN.isNullDN()) && (bindPassword.getValueLength() == 0)) {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.UNWILLING_TO_PERFORM_INT_VALUE, null, ERR_MEM_HANDLER_BIND_SIMPLE_DN_WITHOUT_PASSWORD.get(), null, null));
}
// See if the bind DN is in the set of additional bind credentials. If
// so, then use the password there.
final byte[] additionalCreds = additionalBindCredentials.get(bindDN);
if (additionalCreds != null) {
if (Arrays.equals(additionalCreds, bindPassword.getValue())) {
authenticatedDN = bindDN;
if (controlMap.containsKey(AuthorizationIdentityRequestControl.AUTHORIZATION_IDENTITY_REQUEST_OID)) {
responseControls.add(new AuthorizationIdentityResponseControl("dn:" + bindDN.toString()));
}
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, null, null, null), responseControls);
} else {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.INVALID_CREDENTIALS_INT_VALUE, null, ERR_MEM_HANDLER_BIND_WRONG_PASSWORD.get(request.getBindDN()), null, null));
}
}
// If the target user doesn't exist, then reject the request.
final ReadOnlyEntry userEntry = entryMap.get(bindDN);
if (userEntry == null) {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.INVALID_CREDENTIALS_INT_VALUE, null, ERR_MEM_HANDLER_BIND_NO_SUCH_USER.get(request.getBindDN()), null, null));
}
// Get a list of the user's passwords, restricted to those that match the
// provided clear-text password. If the list is empty, then the
// authentication failed.
final List<InMemoryDirectoryServerPassword> matchingPasswords = getPasswordsInEntry(userEntry, bindPassword);
if (matchingPasswords.isEmpty()) {
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.INVALID_CREDENTIALS_INT_VALUE, null, ERR_MEM_HANDLER_BIND_WRONG_PASSWORD.get(request.getBindDN()), null, null));
}
// If we've gotten here, then authentication was successful.
authenticatedDN = bindDN;
if (controlMap.containsKey(AuthorizationIdentityRequestControl.AUTHORIZATION_IDENTITY_REQUEST_OID)) {
responseControls.add(new AuthorizationIdentityResponseControl("dn:" + bindDN.toString()));
}
return new LDAPMessage(messageID, new BindResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, null, null, null), responseControls);
}
}
use of com.unboundid.ldap.protocol.LDAPMessage in project ldapsdk by pingidentity.
the class InMemoryRequestHandler method processAddRequest.
/**
* Attempts to add an entry to the in-memory data set. The attempt will fail
* if any of the following conditions is true:
* <UL>
* <LI>There is a problem with any of the request controls.</LI>
* <LI>The provided entry has a malformed DN.</LI>
* <LI>The provided entry has the null DN.</LI>
* <LI>The provided entry has a DN that is the same as or subordinate to the
* subschema subentry.</LI>
* <LI>The provided entry has a DN that is the same as or subordinate to the
* changelog base entry.</LI>
* <LI>An entry already exists with the same DN as the entry in the provided
* request.</LI>
* <LI>The entry is outside the set of base DNs for the server.</LI>
* <LI>The entry is below one of the defined base DNs but the immediate
* parent entry does not exist.</LI>
* <LI>If a schema was provided, and the entry is not valid according to the
* constraints of that schema.</LI>
* </UL>
*
* @param messageID The message ID of the LDAP message containing the add
* request.
* @param request The add request that was included in the LDAP message
* that was received.
* @param controls The set of controls included in the LDAP message. It
* may be empty if there were no controls, but will not be
* {@code null}.
*
* @return The {@link LDAPMessage} containing the response to send to the
* client. The protocol op in the {@code LDAPMessage} must be an
* {@code AddResponseProtocolOp}.
*/
@Override()
@NotNull()
public LDAPMessage processAddRequest(final int messageID, @NotNull final AddRequestProtocolOp request, @NotNull final List<Control> controls) {
synchronized (entryMap) {
// Sleep before processing, if appropriate.
sleepBeforeProcessing();
// Process the provided request controls.
final Map<String, Control> controlMap;
try {
controlMap = RequestControlPreProcessor.processControls(LDAPMessage.PROTOCOL_OP_TYPE_ADD_REQUEST, controls);
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new AddResponseProtocolOp(le.getResultCode().intValue(), null, le.getMessage(), null));
}
final ArrayList<Control> responseControls = new ArrayList<>(1);
// If this operation type is not allowed, then reject it.
final boolean isInternalOp = controlMap.containsKey(OID_INTERNAL_OPERATION_REQUEST_CONTROL);
if ((!isInternalOp) && (!config.getAllowedOperationTypes().contains(OperationType.ADD))) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.UNWILLING_TO_PERFORM_INT_VALUE, null, ERR_MEM_HANDLER_ADD_NOT_ALLOWED.get(), null));
}
// client is authenticated.
if ((authenticatedDN.isNullDN() && config.getAuthenticationRequiredOperationTypes().contains(OperationType.ADD))) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.INSUFFICIENT_ACCESS_RIGHTS_INT_VALUE, null, ERR_MEM_HANDLER_ADD_REQUIRES_AUTH.get(), null));
}
// actually doing any further processing.
try {
final ASN1OctetString txnID = processTransactionRequest(messageID, request, controlMap);
if (txnID != null) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, INFO_MEM_HANDLER_OP_IN_TXN.get(txnID.stringValue()), null));
}
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new AddResponseProtocolOp(le.getResultCode().intValue(), le.getMatchedDN(), le.getDiagnosticMessage(), StaticUtils.toList(le.getReferralURLs())), le.getResponseControls());
}
// Get the entry to be added. If a schema was provided, then make sure
// the attributes are created with the appropriate matching rules.
final Entry entry;
final Schema schema = schemaRef.get();
if (schema == null) {
entry = new Entry(request.getDN(), request.getAttributes());
} else {
final List<Attribute> providedAttrs = request.getAttributes();
final List<Attribute> newAttrs = new ArrayList<>(providedAttrs.size());
for (final Attribute a : providedAttrs) {
final String baseName = a.getBaseName();
final MatchingRule matchingRule = MatchingRule.selectEqualityMatchingRule(baseName, schema);
newAttrs.add(new Attribute(a.getName(), matchingRule, a.getRawValues()));
}
entry = new Entry(request.getDN(), schema, newAttrs);
}
// Make sure that the DN is valid.
final DN dn;
try {
dn = entry.getParsedDN();
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.INVALID_DN_SYNTAX_INT_VALUE, null, ERR_MEM_HANDLER_ADD_MALFORMED_DN.get(request.getDN(), le.getMessage()), null));
}
// entry.
if (dn.isNullDN()) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.ENTRY_ALREADY_EXISTS_INT_VALUE, null, ERR_MEM_HANDLER_ADD_ROOT_DSE.get(), null));
} else if (dn.isDescendantOf(subschemaSubentryDN, true)) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.ENTRY_ALREADY_EXISTS_INT_VALUE, null, ERR_MEM_HANDLER_ADD_SCHEMA.get(subschemaSubentryDN.toString()), null));
} else if (dn.isDescendantOf(changeLogBaseDN, true)) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.UNWILLING_TO_PERFORM_INT_VALUE, null, ERR_MEM_HANDLER_ADD_CHANGELOG.get(changeLogBaseDN.toString()), null));
}
// See if there is a referral at or above the target entry.
if (!controlMap.containsKey(ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID)) {
final Entry referralEntry = findNearestReferral(dn);
if (referralEntry != null) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.REFERRAL_INT_VALUE, referralEntry.getDN(), INFO_MEM_HANDLER_REFERRAL_ENCOUNTERED.get(), getReferralURLs(dn, referralEntry)));
}
}
// See if another entry exists with the same DN.
if (entryMap.containsKey(dn)) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.ENTRY_ALREADY_EXISTS_INT_VALUE, null, ERR_MEM_HANDLER_ADD_ALREADY_EXISTS.get(request.getDN()), null));
}
// Make sure that all RDN attribute values are present in the entry.
final RDN rdn = dn.getRDN();
final String[] rdnAttrNames = rdn.getAttributeNames();
final byte[][] rdnAttrValues = rdn.getByteArrayAttributeValues();
for (int i = 0; i < rdnAttrNames.length; i++) {
final MatchingRule matchingRule = MatchingRule.selectEqualityMatchingRule(rdnAttrNames[i], schema);
entry.addAttribute(new Attribute(rdnAttrNames[i], matchingRule, rdnAttrValues[i]));
}
// Make sure that all superior object classes are present in the entry.
if (schema != null) {
final String[] objectClasses = entry.getObjectClassValues();
if (objectClasses != null) {
final LinkedHashMap<String, String> ocMap = new LinkedHashMap<>(StaticUtils.computeMapCapacity(objectClasses.length));
for (final String ocName : objectClasses) {
final ObjectClassDefinition oc = schema.getObjectClass(ocName);
if (oc == null) {
ocMap.put(StaticUtils.toLowerCase(ocName), ocName);
} else {
ocMap.put(StaticUtils.toLowerCase(oc.getNameOrOID()), ocName);
for (final ObjectClassDefinition supClass : oc.getSuperiorClasses(schema, true)) {
ocMap.put(StaticUtils.toLowerCase(supClass.getNameOrOID()), supClass.getNameOrOID());
}
}
}
final String[] newObjectClasses = new String[ocMap.size()];
ocMap.values().toArray(newObjectClasses);
entry.setAttribute("objectClass", newObjectClasses);
}
}
// If a schema was provided, then make sure the entry complies with it.
// Also make sure that there are no attributes marked with
// NO-USER-MODIFICATION.
final EntryValidator entryValidator = entryValidatorRef.get();
if (entryValidator != null) {
final ArrayList<String> invalidReasons = new ArrayList<>(1);
if (!entryValidator.entryIsValid(entry, invalidReasons)) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.OBJECT_CLASS_VIOLATION_INT_VALUE, null, ERR_MEM_HANDLER_ADD_VIOLATES_SCHEMA.get(request.getDN(), StaticUtils.concatenateStrings(invalidReasons)), null));
}
if ((!isInternalOp) && (schema != null) && (!controlMap.containsKey(IgnoreNoUserModificationRequestControl.IGNORE_NO_USER_MODIFICATION_REQUEST_OID))) {
for (final Attribute a : entry.getAttributes()) {
final AttributeTypeDefinition at = schema.getAttributeType(a.getBaseName());
if ((at != null) && at.isNoUserModification()) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.CONSTRAINT_VIOLATION_INT_VALUE, null, ERR_MEM_HANDLER_ADD_CONTAINS_NO_USER_MOD.get(request.getDN(), a.getName()), null));
}
}
}
}
// If the entry contains a proxied authorization control, then process it.
final DN authzDN;
try {
authzDN = handleProxiedAuthControl(controlMap);
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new AddResponseProtocolOp(le.getResultCode().intValue(), null, le.getMessage(), null));
}
// Add a number of operational attributes to the entry.
if (generateOperationalAttributes) {
final Date d = new Date();
if (!entry.hasAttribute("entryDN")) {
entry.addAttribute(new Attribute("entryDN", DistinguishedNameMatchingRule.getInstance(), dn.toNormalizedString()));
}
if (!entry.hasAttribute("entryUUID")) {
entry.addAttribute(new Attribute("entryUUID", CryptoHelper.getRandomUUID().toString()));
}
if (!entry.hasAttribute("subschemaSubentry")) {
entry.addAttribute(new Attribute("subschemaSubentry", DistinguishedNameMatchingRule.getInstance(), subschemaSubentryDN.toString()));
}
if (!entry.hasAttribute("creatorsName")) {
entry.addAttribute(new Attribute("creatorsName", DistinguishedNameMatchingRule.getInstance(), authzDN.toString()));
}
if (!entry.hasAttribute("createTimestamp")) {
entry.addAttribute(new Attribute("createTimestamp", GeneralizedTimeMatchingRule.getInstance(), StaticUtils.encodeGeneralizedTime(d)));
}
if (!entry.hasAttribute("modifiersName")) {
entry.addAttribute(new Attribute("modifiersName", DistinguishedNameMatchingRule.getInstance(), authzDN.toString()));
}
if (!entry.hasAttribute("modifyTimestamp")) {
entry.addAttribute(new Attribute("modifyTimestamp", GeneralizedTimeMatchingRule.getInstance(), StaticUtils.encodeGeneralizedTime(d)));
}
}
// now.
try {
handleAssertionRequestControl(controlMap, entry);
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new AddResponseProtocolOp(le.getResultCode().intValue(), null, le.getMessage(), null));
}
// values are properly encoded.
if ((!passwordEncoders.isEmpty()) && (!configuredPasswordAttributes.isEmpty())) {
final ReadOnlyEntry readOnlyEntry = new ReadOnlyEntry(entry.duplicate());
for (final String passwordAttribute : configuredPasswordAttributes) {
for (final Attribute attr : readOnlyEntry.getAttributesWithOptions(passwordAttribute, null)) {
final ArrayList<byte[]> newValues = new ArrayList<>(attr.size());
for (final ASN1OctetString value : attr.getRawValues()) {
try {
newValues.add(encodeAddPassword(value, readOnlyEntry, Collections.<Modification>emptyList()).getValue());
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.UNWILLING_TO_PERFORM_INT_VALUE, le.getMatchedDN(), le.getMessage(), null));
}
}
final byte[][] newValuesArray = new byte[newValues.size()][];
newValues.toArray(newValuesArray);
entry.setAttribute(new Attribute(attr.getName(), schema, newValuesArray));
}
}
}
// If the request includes the post-read request control, then create the
// appropriate response control.
final PostReadResponseControl postReadResponse = handlePostReadControl(controlMap, entry);
if (postReadResponse != null) {
responseControls.add(postReadResponse);
}
// add the entry.
if (baseDNs.contains(dn)) {
entryMap.put(dn, new ReadOnlyEntry(entry));
indexAdd(entry);
addChangeLogEntry(request, authzDN);
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, null, null), responseControls);
}
// See if the parent entry exists. If so, then we can add the entry.
final DN parentDN = dn.getParent();
if ((parentDN != null) && entryMap.containsKey(parentDN)) {
entryMap.put(dn, new ReadOnlyEntry(entry));
indexAdd(entry);
addChangeLogEntry(request, authzDN);
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null, null, null), responseControls);
}
// within any of the configured base DNs.
for (final DN baseDN : baseDNs) {
if (dn.isDescendantOf(baseDN, true)) {
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.NO_SUCH_OBJECT_INT_VALUE, getMatchedDNString(dn), ERR_MEM_HANDLER_ADD_MISSING_PARENT.get(request.getDN(), dn.getParentString()), null));
}
}
return new LDAPMessage(messageID, new AddResponseProtocolOp(ResultCode.NO_SUCH_OBJECT_INT_VALUE, null, ERR_MEM_HANDLER_ADD_NOT_BELOW_BASE_DN.get(request.getDN()), null));
}
}
use of com.unboundid.ldap.protocol.LDAPMessage in project ldapsdk by pingidentity.
the class InMemoryRequestHandler method processSearchRequest.
/**
* Attempts to process the provided search request. The attempt will fail
* if any of the following conditions is true:
* <UL>
* <LI>There is a problem with any of the request controls.</LI>
* <LI>The modify DN request contains a malformed target DN, new RDN, or
* new superior DN.</LI>
* <LI>The new DN of the entry would conflict with the DN of an existing
* entry.</LI>
* <LI>The new DN of the entry would exist outside the set of defined
* base DNs.</LI>
* <LI>The new DN of the entry is not a defined base DN and does not exist
* immediately below an existing entry.</LI>
* </UL>
*
* @param messageID The message ID of the LDAP message containing the search
* request.
* @param request The search request that was included in the LDAP message
* that was received.
* @param controls The set of controls included in the LDAP message. It
* may be empty if there were no controls, but will not be
* {@code null}.
*
* @return The {@link LDAPMessage} containing the response to send to the
* client. The protocol op in the {@code LDAPMessage} must be an
* {@code SearchResultDoneProtocolOp}.
*/
@Override()
@NotNull()
public LDAPMessage processSearchRequest(final int messageID, @NotNull final SearchRequestProtocolOp request, @NotNull final List<Control> controls) {
synchronized (entryMap) {
final List<SearchResultEntry> entryList = new ArrayList<>(entryMap.size());
final List<SearchResultReference> referenceList = new ArrayList<>(entryMap.size());
final LDAPMessage returnMessage = processSearchRequest(messageID, request, controls, entryList, referenceList);
for (final SearchResultEntry e : entryList) {
try {
connection.sendSearchResultEntry(messageID, e, e.getControls());
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(le.getResultCode().intValue(), le.getMatchedDN(), le.getDiagnosticMessage(), StaticUtils.toList(le.getReferralURLs())), le.getResponseControls());
}
}
for (final SearchResultReference r : referenceList) {
try {
connection.sendSearchResultReference(messageID, new SearchResultReferenceProtocolOp(StaticUtils.toList(r.getReferralURLs())), r.getControls());
} catch (final LDAPException le) {
Debug.debugException(le);
return new LDAPMessage(messageID, new SearchResultDoneProtocolOp(le.getResultCode().intValue(), le.getMatchedDN(), le.getDiagnosticMessage(), StaticUtils.toList(le.getReferralURLs())), le.getResponseControls());
}
}
return returnMessage;
}
}
Aggregations