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