001/*
002 * Copyright 2010-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2010-2020 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-2020 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.StaticUtils;
082import com.unboundid.util.ThreadSafety;
083import com.unboundid.util.ThreadSafetyLevel;
084import com.unboundid.util.Validator;
085
086
087
088/**
089 * This class provides an implementation of a simple LDAP listener request
090 * handler that may be used to forward the request to another LDAP directory
091 * server.
092 */
093@NotMutable()
094@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
095public final class ProxyRequestHandler
096       extends LDAPListenerRequestHandler
097       implements IntermediateResponseListener
098{
099  /**
100   * The serial version UID for this serializable class.
101   */
102  private static final long serialVersionUID = -8714030276701707669L;
103
104
105
106  // The connection to the LDAP server to which requests will be forwarded.
107  private final LDAPConnection ldapConnection;
108
109  // The client connection that has been established.
110  private final LDAPListenerClientConnection listenerConnection;
111
112  // The server set that will be used to establish the connection.
113  private final ServerSet serverSet;
114
115
116
117  /**
118   * Creates a new instance of this proxy request handler that will use the
119   * provided {@link ServerSet} to connect to an LDAP server.
120   *
121   * @param  serverSet  The server that will be used to create LDAP connections
122   *                    to forward any requests received.  It must not be
123   *                    {@code null}.
124   */
125  public ProxyRequestHandler(final ServerSet serverSet)
126  {
127    Validator.ensureNotNull(serverSet);
128
129    this.serverSet = serverSet;
130
131    ldapConnection = null;
132    listenerConnection = null;
133  }
134
135
136
137  /**
138   * Creates a new instance of this proxy request handler with the provided
139   * information.
140   *
141   * @param  serverSet           The server that will be used to create LDAP
142   *                             connections to forward any requests received.
143   *                             It must not be {@code null}.
144   * @param  ldapConnection      The connection to the LDAP server to which
145   *                             requests will be forwarded.
146   * @param  listenerConnection  The client connection with which this request
147   *                             handler is associated.
148   */
149  private ProxyRequestHandler(final ServerSet serverSet,
150               final LDAPConnection ldapConnection,
151               final LDAPListenerClientConnection listenerConnection)
152  {
153    this.serverSet          = serverSet;
154    this.ldapConnection     = ldapConnection;
155    this.listenerConnection = listenerConnection;
156  }
157
158
159
160  /**
161   * {@inheritDoc}
162   */
163  @Override()
164  public ProxyRequestHandler newInstance(
165              final LDAPListenerClientConnection connection)
166         throws LDAPException
167  {
168    return new ProxyRequestHandler(serverSet, serverSet.getConnection(),
169         connection);
170  }
171
172
173
174  /**
175   * {@inheritDoc}
176   */
177  @Override()
178  public void closeInstance()
179  {
180    ldapConnection.close();
181  }
182
183
184
185  /**
186   * {@inheritDoc}
187   */
188  @Override()
189  public LDAPMessage processAddRequest(final int messageID,
190                                       final AddRequestProtocolOp request,
191                                       final List<Control> controls)
192  {
193    final AddRequest addRequest = new AddRequest(request.getDN(),
194         request.getAttributes());
195    if (! controls.isEmpty())
196    {
197      addRequest.setControls(controls);
198    }
199    addRequest.setIntermediateResponseListener(this);
200
201    LDAPResult addResult;
202    try
203    {
204      addResult = ldapConnection.add(addRequest);
205    }
206    catch (final LDAPException le)
207    {
208      Debug.debugException(le);
209      addResult = le.toLDAPResult();
210    }
211
212    final AddResponseProtocolOp addResponseProtocolOp =
213         new AddResponseProtocolOp(addResult.getResultCode().intValue(),
214              addResult.getMatchedDN(), addResult.getDiagnosticMessage(),
215              Arrays.asList(addResult.getReferralURLs()));
216
217    return new LDAPMessage(messageID, addResponseProtocolOp,
218         Arrays.asList(addResult.getResponseControls()));
219  }
220
221
222
223  /**
224   * {@inheritDoc}
225   */
226  @Override()
227  public LDAPMessage processBindRequest(final int messageID,
228                                        final BindRequestProtocolOp request,
229                                        final List<Control> controls)
230  {
231    final Control[] controlArray;
232    if ((controls == null) || (controls.isEmpty()))
233    {
234      controlArray = StaticUtils.NO_CONTROLS;
235    }
236    else
237    {
238      controlArray = new Control[controls.size()];
239      controls.toArray(controlArray);
240    }
241
242    final BindRequest bindRequest;
243    if (request.getCredentialsType() == BindRequestProtocolOp.CRED_TYPE_SIMPLE)
244    {
245      bindRequest = new SimpleBindRequest(request.getBindDN(),
246           request.getSimplePassword().getValue(), controlArray);
247    }
248    else
249    {
250      bindRequest = new GenericSASLBindRequest(request.getBindDN(),
251           request.getSASLMechanism(), request.getSASLCredentials(),
252           controlArray);
253    }
254
255    bindRequest.setIntermediateResponseListener(this);
256
257    LDAPResult bindResult;
258    try
259    {
260      bindResult = ldapConnection.bind(bindRequest);
261    }
262    catch (final LDAPException le)
263    {
264      Debug.debugException(le);
265      bindResult = le.toLDAPResult();
266    }
267
268    final BindResponseProtocolOp bindResponseProtocolOp =
269         new BindResponseProtocolOp(bindResult.getResultCode().intValue(),
270              bindResult.getMatchedDN(), bindResult.getDiagnosticMessage(),
271              Arrays.asList(bindResult.getReferralURLs()), null);
272
273    return new LDAPMessage(messageID, bindResponseProtocolOp,
274         Arrays.asList(bindResult.getResponseControls()));
275  }
276
277
278
279  /**
280   * {@inheritDoc}
281   */
282  @Override()
283  public LDAPMessage processCompareRequest(final int messageID,
284                          final CompareRequestProtocolOp request,
285                          final List<Control> controls)
286  {
287    final CompareRequest compareRequest = new CompareRequest(request.getDN(),
288         request.getAttributeName(), request.getAssertionValue().getValue());
289    if (! controls.isEmpty())
290    {
291      compareRequest.setControls(controls);
292    }
293    compareRequest.setIntermediateResponseListener(this);
294
295    LDAPResult compareResult;
296    try
297    {
298      compareResult = ldapConnection.compare(compareRequest);
299    }
300    catch (final LDAPException le)
301    {
302      Debug.debugException(le);
303      compareResult = le.toLDAPResult();
304    }
305
306    final CompareResponseProtocolOp compareResponseProtocolOp =
307         new CompareResponseProtocolOp(compareResult.getResultCode().intValue(),
308              compareResult.getMatchedDN(),
309              compareResult.getDiagnosticMessage(),
310              Arrays.asList(compareResult.getReferralURLs()));
311
312    return new LDAPMessage(messageID, compareResponseProtocolOp,
313         Arrays.asList(compareResult.getResponseControls()));
314  }
315
316
317
318  /**
319   * {@inheritDoc}
320   */
321  @Override()
322  public LDAPMessage processDeleteRequest(final int messageID,
323                                          final DeleteRequestProtocolOp request,
324                                          final List<Control> controls)
325  {
326    final DeleteRequest deleteRequest = new DeleteRequest(request.getDN());
327    if (! controls.isEmpty())
328    {
329      deleteRequest.setControls(controls);
330    }
331    deleteRequest.setIntermediateResponseListener(this);
332
333    LDAPResult deleteResult;
334    try
335    {
336      deleteResult = ldapConnection.delete(deleteRequest);
337    }
338    catch (final LDAPException le)
339    {
340      Debug.debugException(le);
341      deleteResult = le.toLDAPResult();
342    }
343
344    final DeleteResponseProtocolOp deleteResponseProtocolOp =
345         new DeleteResponseProtocolOp(deleteResult.getResultCode().intValue(),
346              deleteResult.getMatchedDN(), deleteResult.getDiagnosticMessage(),
347              Arrays.asList(deleteResult.getReferralURLs()));
348
349    return new LDAPMessage(messageID, deleteResponseProtocolOp,
350         Arrays.asList(deleteResult.getResponseControls()));
351  }
352
353
354
355  /**
356   * {@inheritDoc}
357   */
358  @Override()
359  public LDAPMessage processExtendedRequest(final int messageID,
360                          final ExtendedRequestProtocolOp request,
361                          final List<Control> controls)
362  {
363    final ExtendedRequest extendedRequest;
364    if (controls.isEmpty())
365    {
366      extendedRequest = new ExtendedRequest(request.getOID(),
367           request.getValue());
368    }
369    else
370    {
371      final Control[] controlArray = new Control[controls.size()];
372      controls.toArray(controlArray);
373      extendedRequest = new ExtendedRequest(request.getOID(),
374           request.getValue(), controlArray);
375    }
376    extendedRequest.setIntermediateResponseListener(this);
377
378    try
379    {
380      final ExtendedResult extendedResult =
381           ldapConnection.processExtendedOperation(extendedRequest);
382
383      final ExtendedResponseProtocolOp extendedResponseProtocolOp =
384           new ExtendedResponseProtocolOp(
385                extendedResult.getResultCode().intValue(),
386                extendedResult.getMatchedDN(),
387                extendedResult.getDiagnosticMessage(),
388                Arrays.asList(extendedResult.getReferralURLs()),
389                extendedResult.getOID(), extendedResult.getValue());
390      return new LDAPMessage(messageID, extendedResponseProtocolOp,
391           Arrays.asList(extendedResult.getResponseControls()));
392    }
393    catch (final LDAPException le)
394    {
395      Debug.debugException(le);
396
397      final ExtendedResponseProtocolOp extendedResponseProtocolOp =
398           new ExtendedResponseProtocolOp(le.getResultCode().intValue(),
399                le.getMatchedDN(), le.getMessage(),
400                Arrays.asList(le.getReferralURLs()), null, null);
401      return new LDAPMessage(messageID, extendedResponseProtocolOp,
402           Arrays.asList(le.getResponseControls()));
403    }
404  }
405
406
407
408  /**
409   * {@inheritDoc}
410   */
411  @Override()
412  public LDAPMessage processModifyRequest(final int messageID,
413                                          final ModifyRequestProtocolOp request,
414                                          final List<Control> controls)
415  {
416    final ModifyRequest modifyRequest = new ModifyRequest(request.getDN(),
417         request.getModifications());
418    if (! controls.isEmpty())
419    {
420      modifyRequest.setControls(controls);
421    }
422    modifyRequest.setIntermediateResponseListener(this);
423
424    LDAPResult modifyResult;
425    try
426    {
427      modifyResult = ldapConnection.modify(modifyRequest);
428    }
429    catch (final LDAPException le)
430    {
431      Debug.debugException(le);
432      modifyResult = le.toLDAPResult();
433    }
434
435    final ModifyResponseProtocolOp modifyResponseProtocolOp =
436         new ModifyResponseProtocolOp(modifyResult.getResultCode().intValue(),
437              modifyResult.getMatchedDN(), modifyResult.getDiagnosticMessage(),
438              Arrays.asList(modifyResult.getReferralURLs()));
439
440    return new LDAPMessage(messageID, modifyResponseProtocolOp,
441         Arrays.asList(modifyResult.getResponseControls()));
442  }
443
444
445
446  /**
447   * {@inheritDoc}
448   */
449  @Override()
450  public LDAPMessage processModifyDNRequest(final int messageID,
451                          final ModifyDNRequestProtocolOp request,
452                          final List<Control> controls)
453  {
454    final ModifyDNRequest modifyDNRequest = new ModifyDNRequest(request.getDN(),
455         request.getNewRDN(), request.deleteOldRDN(),
456         request.getNewSuperiorDN());
457    if (! controls.isEmpty())
458    {
459      modifyDNRequest.setControls(controls);
460    }
461    modifyDNRequest.setIntermediateResponseListener(this);
462
463    LDAPResult modifyDNResult;
464    try
465    {
466      modifyDNResult = ldapConnection.modifyDN(modifyDNRequest);
467    }
468    catch (final LDAPException le)
469    {
470      Debug.debugException(le);
471      modifyDNResult = le.toLDAPResult();
472    }
473
474    final ModifyDNResponseProtocolOp modifyDNResponseProtocolOp =
475         new ModifyDNResponseProtocolOp(
476              modifyDNResult.getResultCode().intValue(),
477              modifyDNResult.getMatchedDN(),
478              modifyDNResult.getDiagnosticMessage(),
479              Arrays.asList(modifyDNResult.getReferralURLs()));
480
481    return new LDAPMessage(messageID, modifyDNResponseProtocolOp,
482         Arrays.asList(modifyDNResult.getResponseControls()));
483  }
484
485
486
487  /**
488   * {@inheritDoc}
489   */
490  @Override()
491  public LDAPMessage processSearchRequest(final int messageID,
492                                          final SearchRequestProtocolOp request,
493                                          final List<Control> controls)
494  {
495    final String[] attrs;
496    final List<String> attrList = request.getAttributes();
497    if (attrList.isEmpty())
498    {
499      attrs = StaticUtils.NO_STRINGS;
500    }
501    else
502    {
503      attrs = new String[attrList.size()];
504      attrList.toArray(attrs);
505    }
506
507    final ProxySearchResultListener searchListener =
508         new ProxySearchResultListener(listenerConnection, messageID);
509
510    final SearchRequest searchRequest = new SearchRequest(searchListener,
511         request.getBaseDN(), request.getScope(), request.getDerefPolicy(),
512         request.getSizeLimit(), request.getTimeLimit(), request.typesOnly(),
513         request.getFilter(), attrs);
514
515    if (! controls.isEmpty())
516    {
517      searchRequest.setControls(controls);
518    }
519    searchRequest.setIntermediateResponseListener(this);
520
521    LDAPResult searchResult;
522    try
523    {
524      searchResult = ldapConnection.search(searchRequest);
525    }
526    catch (final LDAPException le)
527    {
528      Debug.debugException(le);
529      searchResult = le.toLDAPResult();
530    }
531
532    final SearchResultDoneProtocolOp searchResultDoneProtocolOp =
533         new SearchResultDoneProtocolOp(searchResult.getResultCode().intValue(),
534              searchResult.getMatchedDN(), searchResult.getDiagnosticMessage(),
535              Arrays.asList(searchResult.getReferralURLs()));
536
537    return new LDAPMessage(messageID, searchResultDoneProtocolOp,
538         Arrays.asList(searchResult.getResponseControls()));
539  }
540
541
542
543  /**
544   * {@inheritDoc}
545   */
546  @Override()
547  public void intermediateResponseReturned(
548                   final IntermediateResponse intermediateResponse)
549  {
550    try
551    {
552      listenerConnection.sendIntermediateResponse(
553           intermediateResponse.getMessageID(),
554           new IntermediateResponseProtocolOp(intermediateResponse.getOID(),
555                intermediateResponse.getValue()),
556           intermediateResponse.getControls());
557    }
558    catch (final LDAPException le)
559    {
560      Debug.debugException(le);
561    }
562  }
563}