001/*
002 * Copyright 2010-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2010-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2010-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.listener;
037
038
039
040import java.util.Arrays;
041import java.util.List;
042
043import com.unboundid.ldap.protocol.AddRequestProtocolOp;
044import com.unboundid.ldap.protocol.AddResponseProtocolOp;
045import com.unboundid.ldap.protocol.BindRequestProtocolOp;
046import com.unboundid.ldap.protocol.BindResponseProtocolOp;
047import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
048import com.unboundid.ldap.protocol.CompareResponseProtocolOp;
049import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
050import com.unboundid.ldap.protocol.DeleteResponseProtocolOp;
051import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
052import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
053import com.unboundid.ldap.protocol.IntermediateResponseProtocolOp;
054import com.unboundid.ldap.protocol.LDAPMessage;
055import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
056import com.unboundid.ldap.protocol.ModifyResponseProtocolOp;
057import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
058import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp;
059import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
060import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp;
061import com.unboundid.ldap.sdk.AddRequest;
062import com.unboundid.ldap.sdk.BindRequest;
063import com.unboundid.ldap.sdk.CompareRequest;
064import com.unboundid.ldap.sdk.Control;
065import com.unboundid.ldap.sdk.DeleteRequest;
066import com.unboundid.ldap.sdk.ExtendedRequest;
067import com.unboundid.ldap.sdk.ExtendedResult;
068import com.unboundid.ldap.sdk.GenericSASLBindRequest;
069import com.unboundid.ldap.sdk.IntermediateResponse;
070import com.unboundid.ldap.sdk.IntermediateResponseListener;
071import com.unboundid.ldap.sdk.LDAPConnection;
072import com.unboundid.ldap.sdk.LDAPException;
073import com.unboundid.ldap.sdk.LDAPResult;
074import com.unboundid.ldap.sdk.ModifyRequest;
075import com.unboundid.ldap.sdk.ModifyDNRequest;
076import com.unboundid.ldap.sdk.SearchRequest;
077import com.unboundid.ldap.sdk.ServerSet;
078import com.unboundid.ldap.sdk.SimpleBindRequest;
079import com.unboundid.util.Debug;
080import com.unboundid.util.NotMutable;
081import com.unboundid.util.NotNull;
082import com.unboundid.util.Nullable;
083import com.unboundid.util.StaticUtils;
084import com.unboundid.util.ThreadSafety;
085import com.unboundid.util.ThreadSafetyLevel;
086import com.unboundid.util.Validator;
087
088
089
090/**
091 * This class provides an implementation of a simple LDAP listener request
092 * handler that may be used to forward the request to another LDAP directory
093 * server.
094 */
095@NotMutable()
096@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
097public final class ProxyRequestHandler
098       extends LDAPListenerRequestHandler
099       implements IntermediateResponseListener
100{
101  /**
102   * The serial version UID for this serializable class.
103   */
104  private static final long serialVersionUID = -8714030276701707669L;
105
106
107
108  // The connection to the LDAP server to which requests will be forwarded.
109  @Nullable private final LDAPConnection ldapConnection;
110
111  // The client connection that has been established.
112  @Nullable private final LDAPListenerClientConnection listenerConnection;
113
114  // The server set that will be used to establish the connection.
115  @NotNull private final ServerSet serverSet;
116
117
118
119  /**
120   * Creates a new instance of this proxy request handler that will use the
121   * provided {@link ServerSet} to connect to an LDAP server.
122   *
123   * @param  serverSet  The server that will be used to create LDAP connections
124   *                    to forward any requests received.  It must not be
125   *                    {@code null}.
126   */
127  public ProxyRequestHandler(@NotNull final ServerSet serverSet)
128  {
129    Validator.ensureNotNull(serverSet);
130
131    this.serverSet = serverSet;
132
133    ldapConnection = null;
134    listenerConnection = null;
135  }
136
137
138
139  /**
140   * Creates a new instance of this proxy request handler with the provided
141   * information.
142   *
143   * @param  serverSet           The server that will be used to create LDAP
144   *                             connections to forward any requests received.
145   *                             It must not be {@code null}.
146   * @param  ldapConnection      The connection to the LDAP server to which
147   *                             requests will be forwarded.
148   * @param  listenerConnection  The client connection with which this request
149   *                             handler is associated.
150   */
151  private ProxyRequestHandler(@NotNull final ServerSet serverSet,
152               @NotNull final LDAPConnection ldapConnection,
153               @NotNull final LDAPListenerClientConnection listenerConnection)
154  {
155    this.serverSet          = serverSet;
156    this.ldapConnection     = ldapConnection;
157    this.listenerConnection = listenerConnection;
158  }
159
160
161
162  /**
163   * {@inheritDoc}
164   */
165  @Override()
166  @NotNull()
167  public ProxyRequestHandler newInstance(
168              @NotNull final LDAPListenerClientConnection connection)
169         throws LDAPException
170  {
171    return new ProxyRequestHandler(serverSet, serverSet.getConnection(),
172         connection);
173  }
174
175
176
177  /**
178   * {@inheritDoc}
179   */
180  @Override()
181  public void closeInstance()
182  {
183    ldapConnection.close();
184  }
185
186
187
188  /**
189   * {@inheritDoc}
190   */
191  @Override()
192  @NotNull()
193  public LDAPMessage processAddRequest(final int messageID,
194                          @NotNull final AddRequestProtocolOp request,
195                          @NotNull final List<Control> controls)
196  {
197    final AddRequest addRequest = new AddRequest(request.getDN(),
198         request.getAttributes());
199    if (! controls.isEmpty())
200    {
201      addRequest.setControls(controls);
202    }
203    addRequest.setIntermediateResponseListener(this);
204
205    LDAPResult addResult;
206    try
207    {
208      addResult = ldapConnection.add(addRequest);
209    }
210    catch (final LDAPException le)
211    {
212      Debug.debugException(le);
213      addResult = le.toLDAPResult();
214    }
215
216    final AddResponseProtocolOp addResponseProtocolOp =
217         new AddResponseProtocolOp(addResult.getResultCode().intValue(),
218              addResult.getMatchedDN(), addResult.getDiagnosticMessage(),
219              Arrays.asList(addResult.getReferralURLs()));
220
221    return new LDAPMessage(messageID, addResponseProtocolOp,
222         Arrays.asList(addResult.getResponseControls()));
223  }
224
225
226
227  /**
228   * {@inheritDoc}
229   */
230  @Override()
231  @NotNull()
232  public LDAPMessage processBindRequest(final int messageID,
233                          @NotNull final BindRequestProtocolOp request,
234                          @NotNull final List<Control> controls)
235  {
236    final Control[] controlArray;
237    if ((controls == null) || (controls.isEmpty()))
238    {
239      controlArray = StaticUtils.NO_CONTROLS;
240    }
241    else
242    {
243      controlArray = new Control[controls.size()];
244      controls.toArray(controlArray);
245    }
246
247    final BindRequest bindRequest;
248    if (request.getCredentialsType() == BindRequestProtocolOp.CRED_TYPE_SIMPLE)
249    {
250      bindRequest = new SimpleBindRequest(request.getBindDN(),
251           request.getSimplePassword().getValue(), controlArray);
252    }
253    else
254    {
255      bindRequest = new GenericSASLBindRequest(request.getBindDN(),
256           request.getSASLMechanism(), request.getSASLCredentials(),
257           controlArray);
258    }
259
260    bindRequest.setIntermediateResponseListener(this);
261
262    LDAPResult bindResult;
263    try
264    {
265      bindResult = ldapConnection.bind(bindRequest);
266    }
267    catch (final LDAPException le)
268    {
269      Debug.debugException(le);
270      bindResult = le.toLDAPResult();
271    }
272
273    final BindResponseProtocolOp bindResponseProtocolOp =
274         new BindResponseProtocolOp(bindResult.getResultCode().intValue(),
275              bindResult.getMatchedDN(), bindResult.getDiagnosticMessage(),
276              Arrays.asList(bindResult.getReferralURLs()), null);
277
278    return new LDAPMessage(messageID, bindResponseProtocolOp,
279         Arrays.asList(bindResult.getResponseControls()));
280  }
281
282
283
284  /**
285   * {@inheritDoc}
286   */
287  @Override()
288  @NotNull()
289  public LDAPMessage processCompareRequest(final int messageID,
290                          @NotNull final CompareRequestProtocolOp request,
291                          @NotNull final List<Control> controls)
292  {
293    final CompareRequest compareRequest = new CompareRequest(request.getDN(),
294         request.getAttributeName(), request.getAssertionValue().getValue());
295    if (! controls.isEmpty())
296    {
297      compareRequest.setControls(controls);
298    }
299    compareRequest.setIntermediateResponseListener(this);
300
301    LDAPResult compareResult;
302    try
303    {
304      compareResult = ldapConnection.compare(compareRequest);
305    }
306    catch (final LDAPException le)
307    {
308      Debug.debugException(le);
309      compareResult = le.toLDAPResult();
310    }
311
312    final CompareResponseProtocolOp compareResponseProtocolOp =
313         new CompareResponseProtocolOp(compareResult.getResultCode().intValue(),
314              compareResult.getMatchedDN(),
315              compareResult.getDiagnosticMessage(),
316              Arrays.asList(compareResult.getReferralURLs()));
317
318    return new LDAPMessage(messageID, compareResponseProtocolOp,
319         Arrays.asList(compareResult.getResponseControls()));
320  }
321
322
323
324  /**
325   * {@inheritDoc}
326   */
327  @Override()
328  @NotNull()
329  public LDAPMessage processDeleteRequest(final int messageID,
330                          @NotNull final DeleteRequestProtocolOp request,
331                          @NotNull final List<Control> controls)
332  {
333    final DeleteRequest deleteRequest = new DeleteRequest(request.getDN());
334    if (! controls.isEmpty())
335    {
336      deleteRequest.setControls(controls);
337    }
338    deleteRequest.setIntermediateResponseListener(this);
339
340    LDAPResult deleteResult;
341    try
342    {
343      deleteResult = ldapConnection.delete(deleteRequest);
344    }
345    catch (final LDAPException le)
346    {
347      Debug.debugException(le);
348      deleteResult = le.toLDAPResult();
349    }
350
351    final DeleteResponseProtocolOp deleteResponseProtocolOp =
352         new DeleteResponseProtocolOp(deleteResult.getResultCode().intValue(),
353              deleteResult.getMatchedDN(), deleteResult.getDiagnosticMessage(),
354              Arrays.asList(deleteResult.getReferralURLs()));
355
356    return new LDAPMessage(messageID, deleteResponseProtocolOp,
357         Arrays.asList(deleteResult.getResponseControls()));
358  }
359
360
361
362  /**
363   * {@inheritDoc}
364   */
365  @Override()
366  @NotNull()
367  public LDAPMessage processExtendedRequest(final int messageID,
368                          @NotNull final ExtendedRequestProtocolOp request,
369                          @NotNull final List<Control> controls)
370  {
371    final ExtendedRequest extendedRequest;
372    if (controls.isEmpty())
373    {
374      extendedRequest = new ExtendedRequest(request.getOID(),
375           request.getValue());
376    }
377    else
378    {
379      final Control[] controlArray = new Control[controls.size()];
380      controls.toArray(controlArray);
381      extendedRequest = new ExtendedRequest(request.getOID(),
382           request.getValue(), controlArray);
383    }
384    extendedRequest.setIntermediateResponseListener(this);
385
386    try
387    {
388      final ExtendedResult extendedResult =
389           ldapConnection.processExtendedOperation(extendedRequest);
390
391      final ExtendedResponseProtocolOp extendedResponseProtocolOp =
392           new ExtendedResponseProtocolOp(
393                extendedResult.getResultCode().intValue(),
394                extendedResult.getMatchedDN(),
395                extendedResult.getDiagnosticMessage(),
396                Arrays.asList(extendedResult.getReferralURLs()),
397                extendedResult.getOID(), extendedResult.getValue());
398      return new LDAPMessage(messageID, extendedResponseProtocolOp,
399           Arrays.asList(extendedResult.getResponseControls()));
400    }
401    catch (final LDAPException le)
402    {
403      Debug.debugException(le);
404
405      final ExtendedResponseProtocolOp extendedResponseProtocolOp =
406           new ExtendedResponseProtocolOp(le.getResultCode().intValue(),
407                le.getMatchedDN(), le.getMessage(),
408                Arrays.asList(le.getReferralURLs()), null, null);
409      return new LDAPMessage(messageID, extendedResponseProtocolOp,
410           Arrays.asList(le.getResponseControls()));
411    }
412  }
413
414
415
416  /**
417   * {@inheritDoc}
418   */
419  @Override()
420  @NotNull()
421  public LDAPMessage processModifyRequest(final int messageID,
422                         @NotNull final ModifyRequestProtocolOp request,
423                         @NotNull final List<Control> controls)
424  {
425    final ModifyRequest modifyRequest = new ModifyRequest(request.getDN(),
426         request.getModifications());
427    if (! controls.isEmpty())
428    {
429      modifyRequest.setControls(controls);
430    }
431    modifyRequest.setIntermediateResponseListener(this);
432
433    LDAPResult modifyResult;
434    try
435    {
436      modifyResult = ldapConnection.modify(modifyRequest);
437    }
438    catch (final LDAPException le)
439    {
440      Debug.debugException(le);
441      modifyResult = le.toLDAPResult();
442    }
443
444    final ModifyResponseProtocolOp modifyResponseProtocolOp =
445         new ModifyResponseProtocolOp(modifyResult.getResultCode().intValue(),
446              modifyResult.getMatchedDN(), modifyResult.getDiagnosticMessage(),
447              Arrays.asList(modifyResult.getReferralURLs()));
448
449    return new LDAPMessage(messageID, modifyResponseProtocolOp,
450         Arrays.asList(modifyResult.getResponseControls()));
451  }
452
453
454
455  /**
456   * {@inheritDoc}
457   */
458  @Override()
459  @NotNull()
460  public LDAPMessage processModifyDNRequest(final int messageID,
461                          @NotNull final ModifyDNRequestProtocolOp request,
462                          @NotNull final List<Control> controls)
463  {
464    final ModifyDNRequest modifyDNRequest = new ModifyDNRequest(request.getDN(),
465         request.getNewRDN(), request.deleteOldRDN(),
466         request.getNewSuperiorDN());
467    if (! controls.isEmpty())
468    {
469      modifyDNRequest.setControls(controls);
470    }
471    modifyDNRequest.setIntermediateResponseListener(this);
472
473    LDAPResult modifyDNResult;
474    try
475    {
476      modifyDNResult = ldapConnection.modifyDN(modifyDNRequest);
477    }
478    catch (final LDAPException le)
479    {
480      Debug.debugException(le);
481      modifyDNResult = le.toLDAPResult();
482    }
483
484    final ModifyDNResponseProtocolOp modifyDNResponseProtocolOp =
485         new ModifyDNResponseProtocolOp(
486              modifyDNResult.getResultCode().intValue(),
487              modifyDNResult.getMatchedDN(),
488              modifyDNResult.getDiagnosticMessage(),
489              Arrays.asList(modifyDNResult.getReferralURLs()));
490
491    return new LDAPMessage(messageID, modifyDNResponseProtocolOp,
492         Arrays.asList(modifyDNResult.getResponseControls()));
493  }
494
495
496
497  /**
498   * {@inheritDoc}
499   */
500  @Override()
501  @NotNull()
502  public LDAPMessage processSearchRequest(final int messageID,
503                          @NotNull final SearchRequestProtocolOp request,
504                          @NotNull final List<Control> controls)
505  {
506    final String[] attrs;
507    final List<String> attrList = request.getAttributes();
508    if (attrList.isEmpty())
509    {
510      attrs = StaticUtils.NO_STRINGS;
511    }
512    else
513    {
514      attrs = new String[attrList.size()];
515      attrList.toArray(attrs);
516    }
517
518    final ProxySearchResultListener searchListener =
519         new ProxySearchResultListener(listenerConnection, messageID);
520
521    final SearchRequest searchRequest = new SearchRequest(searchListener,
522         request.getBaseDN(), request.getScope(), request.getDerefPolicy(),
523         request.getSizeLimit(), request.getTimeLimit(), request.typesOnly(),
524         request.getFilter(), attrs);
525
526    if (! controls.isEmpty())
527    {
528      searchRequest.setControls(controls);
529    }
530    searchRequest.setIntermediateResponseListener(this);
531
532    LDAPResult searchResult;
533    try
534    {
535      searchResult = ldapConnection.search(searchRequest);
536    }
537    catch (final LDAPException le)
538    {
539      Debug.debugException(le);
540      searchResult = le.toLDAPResult();
541    }
542
543    final SearchResultDoneProtocolOp searchResultDoneProtocolOp =
544         new SearchResultDoneProtocolOp(searchResult.getResultCode().intValue(),
545              searchResult.getMatchedDN(), searchResult.getDiagnosticMessage(),
546              Arrays.asList(searchResult.getReferralURLs()));
547
548    return new LDAPMessage(messageID, searchResultDoneProtocolOp,
549         Arrays.asList(searchResult.getResponseControls()));
550  }
551
552
553
554  /**
555   * {@inheritDoc}
556   */
557  @Override()
558  public void intermediateResponseReturned(
559                   @NotNull final IntermediateResponse intermediateResponse)
560  {
561    try
562    {
563      listenerConnection.sendIntermediateResponse(
564           intermediateResponse.getMessageID(),
565           new IntermediateResponseProtocolOp(intermediateResponse.getOID(),
566                intermediateResponse.getValue()),
567           intermediateResponse.getControls());
568    }
569    catch (final LDAPException le)
570    {
571      Debug.debugException(le);
572    }
573  }
574}