001/*
002 * Copyright 2008-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.controls;
022
023
024
025import java.io.Serializable;
026import java.util.ArrayList;
027
028import com.unboundid.asn1.ASN1Boolean;
029import com.unboundid.asn1.ASN1Constants;
030import com.unboundid.asn1.ASN1Element;
031import com.unboundid.asn1.ASN1OctetString;
032import com.unboundid.asn1.ASN1Sequence;
033import com.unboundid.ldap.sdk.LDAPException;
034import com.unboundid.ldap.sdk.ResultCode;
035import com.unboundid.util.Debug;
036import com.unboundid.util.NotMutable;
037import com.unboundid.util.StaticUtils;
038import com.unboundid.util.ThreadSafety;
039import com.unboundid.util.ThreadSafetyLevel;
040
041import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
042
043
044
045/**
046 * This class implements a data structure which encapsulates the value of an
047 * intermediate client request value.  It may recursively embed intermediate
048 * client request values from downstream clients.
049 * <BR>
050 * <BLOCKQUOTE>
051 *   <B>NOTE:</B>  This class, and other classes within the
052 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
053 *   supported for use against Ping Identity, UnboundID, and
054 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
055 *   for proprietary functionality or for external specifications that are not
056 *   considered stable or mature enough to be guaranteed to work in an
057 *   interoperable way with other types of LDAP servers.
058 * </BLOCKQUOTE>
059 * <BR>
060 * See the documentation in the {@link IntermediateClientRequestControl} class
061 * for an example of using the intermediate client request and response
062 * controls.
063 */
064@NotMutable()
065@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
066public final class IntermediateClientRequestValue
067       implements Serializable
068{
069  /**
070   * The BER type for the downstreamRequest element.
071   */
072  private static final byte TYPE_DOWNSTREAM_REQUEST = (byte) 0xA0;
073
074
075
076  /**
077   * The BER type for the downstreamClientAddress element.
078   */
079  private static final byte TYPE_DOWNSTREAM_CLIENT_ADDRESS = (byte) 0x81;
080
081
082
083  /**
084   * The BER type for the downstreamClientSecure element.
085   */
086  private static final byte TYPE_DOWNSTREAM_CLIENT_SECURE = (byte) 0x82;
087
088
089
090  /**
091   * The BER type for the clientIdentity element.
092   */
093  private static final byte TYPE_CLIENT_IDENTITY = (byte) 0x83;
094
095
096
097  /**
098   * The BER type for the clientName element.
099   */
100  private static final byte TYPE_CLIENT_NAME = (byte) 0x84;
101
102
103
104  /**
105   * The BER type for the clientSessionID element.
106   */
107  private static final byte TYPE_CLIENT_SESSION_ID = (byte) 0x85;
108
109
110
111  /**
112   * The BER type for the clientRequestID element.
113   */
114  private static final byte TYPE_CLIENT_REQUEST_ID = (byte) 0x86;
115
116
117
118  /**
119   * The serial version UID for this serializable class.
120   */
121  private static final long serialVersionUID = -794887520013838259L;
122
123
124
125  // Indicates whether the communication with the downstream client is secure.
126  private final Boolean downstreamClientSecure;
127
128  // The downstream request value, if present.
129  private final IntermediateClientRequestValue downstreamRequest;
130
131  // The requested client authorization identity, if present.
132  private final String clientIdentity;
133
134  // The downstream client address, if present.
135  private final String downstreamClientAddress;
136
137  // The client name, which describes the client application, if present.
138  private final String clientName;
139
140  // The client request ID, if present.
141  private final String clientRequestID;
142
143  // The client session ID, if present.
144  private final String clientSessionID;
145
146
147
148  /**
149   * Creates a new intermediate client request value with the provided
150   * information.
151   *
152   * @param  downstreamRequest        A wrapped intermediate client request from
153   *                                  a downstream client.  It may be
154   *                                  {@code null} if there is no downstream
155   *                                  request.
156   * @param  downstreamClientAddress  The IP address or resolvable name of the
157   *                                  downstream client system.  It may be
158   *                                  {@code null} if there is no downstream
159   *                                  client or its address is not available.
160   * @param  downstreamClientSecure   Indicates whether communication with the
161   *                                  downstream client is secure.  It may be
162   *                                  {@code null} if there is no downstream
163   *                                  client or it is not known whether the
164   *                                  communication is secure.
165   * @param  clientIdentity           The requested client authorization
166   *                                  identity.  It may be {@code null} if there
167   *                                  is no requested authorization identity.
168   * @param  clientName               An identifier string that summarizes the
169   *                                  client application that created this
170   *                                  intermediate client request.  It may be
171   *                                  {@code null} if that information is not
172   *                                  available.
173   * @param  clientSessionID          A string that may be used to identify the
174   *                                  session in the client application.  It may
175   *                                  be {@code null} if there is no available
176   *                                  session identifier.
177   * @param  clientRequestID          A string that may be used to identify the
178   *                                  request in the client application.  It may
179   *                                  be {@code null} if there is no available
180   *                                  request identifier.
181   */
182  public IntermediateClientRequestValue(
183              final IntermediateClientRequestValue downstreamRequest,
184              final String downstreamClientAddress,
185              final Boolean downstreamClientSecure, final String clientIdentity,
186              final String clientName, final String clientSessionID,
187              final String clientRequestID)
188  {
189    this.downstreamRequest       = downstreamRequest;
190    this.downstreamClientAddress = downstreamClientAddress;
191    this.downstreamClientSecure  = downstreamClientSecure;
192    this.clientIdentity          = clientIdentity;
193    this.clientName              = clientName;
194    this.clientSessionID         = clientSessionID;
195    this.clientRequestID         = clientRequestID;
196  }
197
198
199
200  /**
201   * Retrieves the wrapped request from a downstream client, if available.
202   *
203   * @return  The wrapped request from a downstream client, or {@code null} if
204   *          there is none.
205   */
206  public IntermediateClientRequestValue getDownstreamRequest()
207  {
208    return downstreamRequest;
209  }
210
211
212
213  /**
214   * Retrieves the requested client authorization identity, if available.
215   *
216   * @return  The requested client authorization identity, or {@code null} if
217   *          there is none.
218   */
219  public String getClientIdentity()
220  {
221    return clientIdentity;
222  }
223
224
225
226  /**
227   * Retrieves the IP address or resolvable name of the downstream client
228   * system, if available.
229   *
230   * @return  The IP address or resolvable name of the downstream client system,
231   *          or {@code null} if there is no downstream client or its address is
232   *          not available.
233   */
234  public String getDownstreamClientAddress()
235  {
236    return downstreamClientAddress;
237  }
238
239
240
241  /**
242   * Indicates whether the communication with the communication with the
243   * downstream client is secure (i.e., whether communication between the
244   * client application and the downstream client is safe from interpretation or
245   * undetectable alteration by a third party observer or interceptor).
246   *
247   *
248   * @return  {@code Boolean.TRUE} if communication with the downstream client
249   *          is secure, {@code Boolean.FALSE} if it is not secure, or
250   *          {@code null} if there is no downstream client or it is not known
251   *          whether the communication is secure.
252   */
253  public Boolean downstreamClientSecure()
254  {
255    return downstreamClientSecure;
256  }
257
258
259
260  /**
261   * Retrieves a string that identifies the client application that created this
262   * intermediate client request value.
263   *
264   * @return  A string that may be used to identify the client application that
265   *          created this intermediate client request value.
266   */
267  public String getClientName()
268  {
269    return clientName;
270  }
271
272
273
274  /**
275   * Retrieves a string that may be used to identify the session in the client
276   * application.
277   *
278   * @return  A string that may be used to identify the session in the client
279   *          application, or {@code null} if there is none.
280   */
281  public String getClientSessionID()
282  {
283    return clientSessionID;
284  }
285
286
287
288  /**
289   * Retrieves a string that may be used to identify the request in the client
290   * application.
291   *
292   * @return  A string that may be used to identify the request in the client
293   *          application, or {@code null} if there is none.
294   */
295  public String getClientRequestID()
296  {
297    return clientRequestID;
298  }
299
300
301
302  /**
303   * Encodes this intermediate client request value to a form that may be
304   * included in the request control.
305   *
306   * @return  An ASN.1 octet string containing the encoded client request value.
307   */
308  public ASN1Sequence encode()
309  {
310    return encode(ASN1Constants.UNIVERSAL_SEQUENCE_TYPE);
311  }
312
313
314
315  /**
316   * Encodes this intermediate client request value to a form that may be
317   * included in the request control.
318   *
319   * @param  type  The BER type to use for this element.
320   *
321   * @return  An ASN.1 octet string containing the encoded client request value.
322   */
323  private ASN1Sequence encode(final byte type)
324  {
325    final ArrayList<ASN1Element> elements = new ArrayList<>(7);
326
327    if (downstreamRequest != null)
328    {
329      elements.add(downstreamRequest.encode(TYPE_DOWNSTREAM_REQUEST));
330    }
331
332    if (downstreamClientAddress != null)
333    {
334      elements.add(new ASN1OctetString(TYPE_DOWNSTREAM_CLIENT_ADDRESS,
335                                       downstreamClientAddress));
336    }
337
338    if (downstreamClientSecure != null)
339    {
340      elements.add(new ASN1Boolean(TYPE_DOWNSTREAM_CLIENT_SECURE,
341                                   downstreamClientSecure));
342    }
343
344    if (clientIdentity != null)
345    {
346      elements.add(new ASN1OctetString(TYPE_CLIENT_IDENTITY, clientIdentity));
347    }
348
349    if (clientName != null)
350    {
351      elements.add(new ASN1OctetString(TYPE_CLIENT_NAME,  clientName));
352    }
353
354    if (clientSessionID != null)
355    {
356      elements.add(new ASN1OctetString(TYPE_CLIENT_SESSION_ID,
357                                       clientSessionID));
358    }
359
360    if (clientRequestID != null)
361    {
362      elements.add(new ASN1OctetString(TYPE_CLIENT_REQUEST_ID,
363                                       clientRequestID));
364    }
365
366    return new ASN1Sequence(type, elements);
367  }
368
369
370
371  /**
372   * Decodes the provided ASN.1 sequence as an intermediate client request
373   * value.
374   *
375   * @param  sequence  The sequence to be decoded as an intermediate client
376   *                   request value.
377   *
378   * @return  The decoded intermediate client request value.
379   *
380   * @throws  LDAPException  If the provided sequence cannot be decoded as an
381   *                         intermediate client request value.
382   */
383  public static IntermediateClientRequestValue
384                     decode(final ASN1Sequence sequence)
385         throws LDAPException
386  {
387    Boolean                        downstreamClientSecure  = null;
388    IntermediateClientRequestValue downstreamRequest       = null;
389    String                         clientIdentity          = null;
390    String                         downstreamClientAddress = null;
391    String                         clientName              = null;
392    String                         clientRequestID         = null;
393    String                         clientSessionID         = null;
394
395    for (final ASN1Element element : sequence.elements())
396    {
397      switch (element.getType())
398      {
399        case TYPE_DOWNSTREAM_REQUEST:
400          try
401          {
402            final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element);
403            downstreamRequest = decode(s);
404          }
405          catch (final LDAPException le)
406          {
407            Debug.debugException(le);
408            throw new LDAPException(ResultCode.DECODING_ERROR,
409                 ERR_ICREQ_CANNOT_DECODE_DOWNSTREAM_REQUEST.get(
410                      le.getMessage()), le);
411          }
412          catch (final Exception e)
413          {
414            Debug.debugException(e);
415            throw new LDAPException(ResultCode.DECODING_ERROR,
416                 ERR_ICREQ_CANNOT_DECODE_DOWNSTREAM_REQUEST.get(
417                      StaticUtils.getExceptionMessage(e)),
418                 e);
419          }
420          break;
421
422        case TYPE_DOWNSTREAM_CLIENT_ADDRESS:
423          downstreamClientAddress =
424               ASN1OctetString.decodeAsOctetString(element).stringValue();
425          break;
426
427        case TYPE_DOWNSTREAM_CLIENT_SECURE:
428          try
429          {
430            downstreamClientSecure =
431                 ASN1Boolean.decodeAsBoolean(element).booleanValue();
432          }
433          catch (final Exception e)
434          {
435            Debug.debugException(e);
436            throw new LDAPException(ResultCode.DECODING_ERROR,
437                 ERR_ICREQ_CANNOT_DECODE_DOWNSTREAM_SECURE.get(
438                      StaticUtils.getExceptionMessage(e)),
439                 e);
440          }
441          break;
442
443        case TYPE_CLIENT_IDENTITY:
444          clientIdentity =
445               ASN1OctetString.decodeAsOctetString(element).stringValue();
446          break;
447
448        case TYPE_CLIENT_NAME:
449          clientName =
450               ASN1OctetString.decodeAsOctetString(element).stringValue();
451          break;
452
453        case TYPE_CLIENT_SESSION_ID:
454          clientSessionID =
455               ASN1OctetString.decodeAsOctetString(element).stringValue();
456          break;
457
458        case TYPE_CLIENT_REQUEST_ID:
459          clientRequestID =
460               ASN1OctetString.decodeAsOctetString(element).stringValue();
461          break;
462
463        default:
464          throw new LDAPException(ResultCode.DECODING_ERROR,
465               ERR_ICREQ_INVALID_ELEMENT_TYPE.get(
466                    StaticUtils.toHex(element.getType())));
467      }
468    }
469
470    return new IntermediateClientRequestValue(downstreamRequest,
471                                              downstreamClientAddress,
472                                              downstreamClientSecure,
473                                              clientIdentity, clientName,
474                                              clientSessionID, clientRequestID);
475  }
476
477
478
479  /**
480   * Generates a hash code for this intermediate client request value.
481   *
482   * @return  A hash code for this intermediate client request value.
483   */
484  @Override()
485  public int hashCode()
486  {
487    int hashCode = 0;
488
489    if (downstreamRequest != null)
490    {
491      hashCode += downstreamRequest.hashCode();
492    }
493
494    if (downstreamClientAddress != null)
495    {
496      hashCode += downstreamClientAddress.hashCode();
497    }
498
499    if (downstreamClientSecure != null)
500    {
501      hashCode += downstreamClientSecure.hashCode();
502    }
503
504    if (clientIdentity != null)
505    {
506      hashCode += clientIdentity.hashCode();
507    }
508
509    if (clientName != null)
510    {
511      hashCode += clientName.hashCode();
512    }
513
514    if (clientSessionID != null)
515    {
516      hashCode += clientSessionID.hashCode();
517    }
518
519    if (clientRequestID != null)
520    {
521      hashCode += clientRequestID.hashCode();
522    }
523
524    return hashCode;
525  }
526
527
528
529  /**
530   * Indicates whether the provided object is equal to this intermediate client
531   * request value.  It will only be considered equal if the provided object is
532   * also an intermediate client request value with all the same fields.
533   *
534   * @param  o  The object for which to make the determination.
535   *
536   * @return  {@code true} if the provided object is considered equal to this
537   *          intermediate client request value, or {@code false} if not.
538   */
539  @Override()
540  public boolean equals(final Object o)
541  {
542    if (o == this)
543    {
544      return true;
545    }
546    else if (o == null)
547    {
548      return false;
549    }
550    else if (! (o instanceof IntermediateClientRequestValue))
551    {
552      return false;
553    }
554
555    final IntermediateClientRequestValue v = (IntermediateClientRequestValue) o;
556
557    if (downstreamRequest == null)
558    {
559      if (v.downstreamRequest != null)
560      {
561        return false;
562      }
563    }
564    else
565    {
566      if (! downstreamRequest.equals(v.downstreamRequest))
567      {
568        return false;
569      }
570    }
571
572    if (downstreamClientAddress == null)
573    {
574      if (v.downstreamClientAddress != null)
575      {
576        return false;
577      }
578    }
579    else
580    {
581      if (! downstreamClientAddress.equals(v.downstreamClientAddress))
582      {
583        return false;
584      }
585    }
586
587    if (downstreamClientSecure == null)
588    {
589      if (v.downstreamClientSecure != null)
590      {
591        return false;
592      }
593    }
594    else
595    {
596      if (! downstreamClientSecure.equals(v.downstreamClientSecure))
597      {
598        return false;
599      }
600    }
601
602    if (clientIdentity == null)
603    {
604      if (v.clientIdentity != null)
605      {
606        return false;
607      }
608    }
609    else
610    {
611      if (! clientIdentity.equals(v.clientIdentity))
612      {
613        return false;
614      }
615    }
616
617    if (clientName == null)
618    {
619      if (v.clientName != null)
620      {
621        return false;
622      }
623    }
624    else
625    {
626      if (! clientName.equals(v.clientName))
627      {
628        return false;
629      }
630    }
631
632    if (clientSessionID == null)
633    {
634      if (v.clientSessionID != null)
635      {
636        return false;
637      }
638    }
639    else
640    {
641      if (! clientSessionID.equals(v.clientSessionID))
642      {
643        return false;
644      }
645    }
646
647    if (clientRequestID == null)
648    {
649      if (v.clientRequestID != null)
650      {
651        return false;
652      }
653    }
654    else
655    {
656      if (! clientRequestID.equals(v.clientRequestID))
657      {
658        return false;
659      }
660    }
661
662    return true;
663  }
664
665
666
667  /**
668   * Retrieves a string representation of this intermediate client request
669   * value.
670   *
671   * @return  A string representation of this intermediate client request value.
672   */
673  @Override()
674  public String toString()
675  {
676    final StringBuilder buffer = new StringBuilder();
677    toString(buffer);
678    return buffer.toString();
679  }
680
681
682
683  /**
684   * Appends a string representation of this intermediate client request value
685   * to the provided buffer.
686   *
687   * @param  buffer  The buffer to which the information is to be appended.
688   */
689  public void toString(final StringBuilder buffer)
690  {
691    buffer.append("IntermediateClientRequestValue(");
692
693    boolean added = false;
694    if (downstreamRequest != null)
695    {
696      buffer.append("downstreamRequest=");
697      downstreamRequest.toString(buffer);
698      added = true;
699    }
700
701    if (clientIdentity != null)
702    {
703      if (added)
704      {
705        buffer.append(", ");
706      }
707      else
708      {
709        added = true;
710      }
711
712      buffer.append("clientIdentity='");
713      buffer.append(clientIdentity);
714      buffer.append('\'');
715    }
716
717    if (downstreamClientAddress != null)
718    {
719      if (added)
720      {
721        buffer.append(", ");
722      }
723      else
724      {
725        added = true;
726      }
727
728      buffer.append("downstreamClientAddress='");
729      buffer.append(downstreamClientAddress);
730      buffer.append('\'');
731    }
732
733    if (downstreamClientSecure != null)
734    {
735      if (added)
736      {
737        buffer.append(", ");
738      }
739      else
740      {
741        added = true;
742      }
743
744      buffer.append("downstreamClientSecure='");
745      buffer.append(downstreamClientSecure);
746      buffer.append('\'');
747    }
748
749    if (clientName != null)
750    {
751      if (added)
752      {
753        buffer.append(", ");
754      }
755      else
756      {
757        added = true;
758      }
759
760      buffer.append("clientName='");
761      buffer.append(clientName);
762      buffer.append('\'');
763    }
764
765    if (clientSessionID != null)
766    {
767      if (added)
768      {
769        buffer.append(", ");
770      }
771      else
772      {
773        added = true;
774      }
775
776      buffer.append("clientSessionID='");
777      buffer.append(clientSessionID);
778      buffer.append('\'');
779    }
780
781    if (clientRequestID != null)
782    {
783      if (added)
784      {
785        buffer.append(", ");
786      }
787      else
788      {
789        added = true;
790      }
791
792      buffer.append("clientRequestID='");
793      buffer.append(clientRequestID);
794      buffer.append('\'');
795    }
796
797    buffer.append(')');
798  }
799}