001/*
002 * Copyright 2008-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-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) 2008-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.sdk.unboundidds.controls;
037
038
039
040import java.util.LinkedHashMap;
041import java.util.List;
042import java.util.Map;
043
044import com.unboundid.asn1.ASN1Element;
045import com.unboundid.asn1.ASN1OctetString;
046import com.unboundid.asn1.ASN1Sequence;
047import com.unboundid.ldap.sdk.Control;
048import com.unboundid.ldap.sdk.JSONControlDecodeHelper;
049import com.unboundid.ldap.sdk.LDAPException;
050import com.unboundid.ldap.sdk.ResultCode;
051import com.unboundid.util.NotMutable;
052import com.unboundid.util.NotNull;
053import com.unboundid.util.Nullable;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056import com.unboundid.util.json.JSONBoolean;
057import com.unboundid.util.json.JSONField;
058import com.unboundid.util.json.JSONObject;
059import com.unboundid.util.json.JSONString;
060import com.unboundid.util.json.JSONValue;
061
062import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
063
064
065
066/**
067 * This class defines an intermediate client request control, which can be used
068 * to provide a server with information about the client and any downstream
069 * clients that it may have.  It can be used to help trace operations from the
070 * client to the directory server, potentially through any intermediate hops
071 * (like proxy servers) that may also support the intermediate client controls.
072 * <BR>
073 * <BLOCKQUOTE>
074 *   <B>NOTE:</B>  This class, and other classes within the
075 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
076 *   supported for use against Ping Identity, UnboundID, and
077 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
078 *   for proprietary functionality or for external specifications that are not
079 *   considered stable or mature enough to be guaranteed to work in an
080 *   interoperable way with other types of LDAP servers.
081 * </BLOCKQUOTE>
082 * <BR>
083 * This control is not based on any public standard.  It was originally
084 * developed for use with the Ping Identity, UnboundID, and Nokia/Alcatel-Lucent
085 * 8661 Directory Server.  The value of this control uses the following
086 * encoding:
087 * <BR><BR>
088 * <PRE>
089 * IntermediateClientRequest ::= SEQUENCE {
090 *      downstreamRequest        [0] IntermediateClientRequest OPTIONAL,
091 *      downstreamClientAddress  [1] OCTET STRING OPTIONAL,
092 *      downstreamClientSecure   [2] BOOLEAN DEFAULT FALSE,
093 *      clientIdentity           [3] authzId OPTIONAL,
094 *      clientName               [4] OCTET STRING OPTIONAL,
095 *      clientSessionID          [5] OCTET STRING OPTIONAL,
096 *      clientRequestID          [6] OCTET STRING OPTIONAL,
097 *      ... }
098 * </PRE>
099 * <H2>Example</H2>
100 * The following example demonstrates the use of the intermediate client
101 * controls to perform a search operation in the directory server.  The request
102 * will be from an application named "my client" with a session ID of
103 * "session123" and a request ID of "request456":
104 * <PRE>
105 * SearchRequest searchRequest = new SearchRequest("dc=example,dc=com",
106 *      SearchScope.SUB, Filter.createEqualityFilter("uid", "john.doe"));
107 * searchRequest.addControl(new IntermediateClientRequestControl(null, null,
108 *      null, null, "my client", "session123", "request456"));
109 * SearchResult searchResult = connection.search(searchRequest);
110 *
111 * IntermediateClientResponseControl c =
112 *      IntermediateClientResponseControl.get(searchResult);
113 * if (c != null)
114 * {
115 *   // There was an intermediate client response control.
116 *   IntermediateClientResponseValue responseValue = c.getResponseValue();
117 * }
118 * </PRE>
119 */
120@NotMutable()
121@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
122public final class IntermediateClientRequestControl
123       extends Control
124{
125  /**
126   * The OID (1.3.6.1.4.1.30221.2.5.2) for the intermediate client request
127   * control.
128   */
129  @NotNull public static final String INTERMEDIATE_CLIENT_REQUEST_OID =
130       "1.3.6.1.4.1.30221.2.5.2";
131
132
133
134  /**
135   * The name of the field used to hold the client identity in the JSON
136   * representation of this control.
137   */
138  @NotNull private static final String JSON_FIELD_CLIENT_IDENTITY =
139       "client-identity";
140
141
142
143  /**
144   * The name of the field used to hold the client name in the JSON
145   * representation of this control.
146   */
147  @NotNull private static final String JSON_FIELD_CLIENT_NAME = "client-name";
148
149
150
151  /**
152   * The name of the field used to hold the client request ID in the JSON
153   * representation of this control.
154   */
155  @NotNull private static final String JSON_FIELD_CLIENT_REQUEST_ID =
156       "client-request-id";
157
158
159
160  /**
161   * The name of the field used to hold the client session ID in the JSON
162   * representation of this control.
163   */
164  @NotNull private static final String JSON_FIELD_CLIENT_SESSION_ID =
165       "client-session-id";
166
167
168
169  /**
170   * The name of the field used to hold the downstream client address in the
171   * JSON representation of this control.
172   */
173  @NotNull private static final String JSON_FIELD_DOWNSTREAM_CLIENT_ADDRESS =
174       "downstream-client-address";
175
176
177
178  /**
179   * The name of the field used to hold the downstream client secure flag in the
180   * JSON representation of this control.
181   */
182  @NotNull private static final String JSON_FIELD_DOWNSTREAM_CLIENT_SECURE =
183       "downstream-client-secure";
184
185
186
187  /**
188   * The name of the field used to hold the downstream request in the JSON
189   * representation of this control.
190   */
191  @NotNull private static final String JSON_FIELD_DOWNSTREAM_REQUEST =
192       "downstream-request";
193
194
195
196  /**
197   * The serial version UID for this serializable class.
198   */
199  private static final long serialVersionUID = 4883725840393001578L;
200
201
202
203  // The value for this intermediate client request control.
204  @NotNull private final IntermediateClientRequestValue value;
205
206
207
208  /**
209   * Creates a new intermediate client request control with the provided
210   * information.  It will be marked critical.
211   *
212   * @param  downstreamRequest        A wrapped intermediate client request from
213   *                                  a downstream client.  It may be
214   *                                  {@code null} if there is no downstream
215   *                                  request.
216   * @param  downstreamClientAddress  The IP address or resolvable name of the
217   *                                  downstream client system.  It may be
218   *                                  {@code null} if there is no downstream
219   *                                  client or its address is not available.
220   * @param  downstreamClientSecure   Indicates whether communication with the
221   *                                  downstream client is secure.  It may be
222   *                                  {@code null} if there is no downstream
223   *                                  client or it is not known whether the
224   *                                  communication is secure.
225   * @param  clientIdentity           The requested client authorization
226   *                                  identity.  It may be {@code null} if there
227   *                                  is no requested authorization identity.
228   * @param  clientName               An identifier string that summarizes the
229   *                                  client application that created this
230   *                                  intermediate client request.  It may be
231   *                                  {@code null} if that information is not
232   *                                  available.
233   * @param  clientSessionID          A string that may be used to identify the
234   *                                  session in the client application.  It may
235   *                                  be {@code null} if there is no available
236   *                                  session identifier.
237   * @param  clientRequestID          A string that may be used to identify the
238   *                                  request in the client application.  It may
239   *                                  be {@code null} if there is no available
240   *                                  request identifier.
241   */
242  public IntermediateClientRequestControl(
243              @Nullable final IntermediateClientRequestValue downstreamRequest,
244              @Nullable final String downstreamClientAddress,
245              @Nullable final Boolean downstreamClientSecure,
246              @Nullable final String clientIdentity,
247              @Nullable final String clientName,
248              @Nullable final String clientSessionID,
249              @Nullable final String clientRequestID)
250  {
251    this(true,
252         new IntermediateClientRequestValue(downstreamRequest,
253                  downstreamClientAddress, downstreamClientSecure,
254                  clientIdentity, clientName, clientSessionID,
255                  clientRequestID));
256  }
257
258
259
260  /**
261   * Creates a new intermediate client request control with the provided value.
262   * It will be marked critical.
263   *
264   * @param  value  The value to use for this intermediate client request
265   *                control.  It must not be {@code null}.
266   */
267  public IntermediateClientRequestControl(
268              @NotNull final IntermediateClientRequestValue value)
269  {
270    this(true, value);
271  }
272
273
274
275  /**
276   * Creates a new intermediate client request control with the provided value.
277   *
278   * @param  isCritical  Indicates whether the control should be marked
279   *                     critical.
280   * @param  value       The value to use for this intermediate client request
281   *                     control.  It must not be {@code null}.
282   */
283  public IntermediateClientRequestControl(final boolean isCritical,
284              @NotNull final IntermediateClientRequestValue value)
285  {
286    super(INTERMEDIATE_CLIENT_REQUEST_OID, isCritical,
287          new ASN1OctetString(value.encode().encode()));
288
289    this.value = value;
290  }
291
292
293
294  /**
295   * Creates a new intermediate client request control which is decoded from the
296   * provided generic control.
297   *
298   * @param  control  The generic control to be decoded as an intermediate
299   *                  client request control.
300   *
301   * @throws  LDAPException  If the provided control cannot be decoded as an
302   *                         intermediate client request control.
303   */
304  public IntermediateClientRequestControl(@NotNull final Control control)
305         throws LDAPException
306  {
307    super(control);
308
309    final ASN1OctetString controlValue = control.getValue();
310    if (controlValue == null)
311    {
312      throw new LDAPException(ResultCode.DECODING_ERROR,
313                              ERR_ICREQ_CONTROL_NO_VALUE.get());
314    }
315
316    final ASN1Sequence valueSequence;
317    try
318    {
319      final ASN1Element valueElement =
320           ASN1Element.decode(controlValue.getValue());
321      valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
322    }
323    catch (final Exception e)
324    {
325      throw new LDAPException(ResultCode.DECODING_ERROR,
326                              ERR_ICREQ_CONTROL_VALUE_NOT_SEQUENCE.get(e), e);
327    }
328
329    value = IntermediateClientRequestValue.decode(valueSequence);
330  }
331
332
333
334  /**
335   * Retrieves the value for this intermediate client request.
336   *
337   * @return  The value for this intermediate client request.
338   */
339  @NotNull()
340  public IntermediateClientRequestValue getRequestValue()
341  {
342    return value;
343  }
344
345
346
347  /**
348   * Retrieves the wrapped request from a downstream client, if available.
349   *
350   * @return  The wrapped request from a downstream client, or {@code null} if
351   *          there is none.
352   */
353  @Nullable()
354  public IntermediateClientRequestValue getDownstreamRequest()
355  {
356    return value.getDownstreamRequest();
357  }
358
359
360
361  /**
362   * Retrieves the requested client authorization identity, if available.
363   *
364   * @return  The requested client authorization identity, or {@code null} if
365   *          there is none.
366   */
367  @Nullable()
368  public String getClientIdentity()
369  {
370    return value.getClientIdentity();
371  }
372
373
374
375  /**
376   * Retrieves the IP address or resolvable name of the downstream client
377   * system, if available.
378   *
379   * @return  The IP address or resolvable name of the downstream client system,
380   *          or {@code null} if there is no downstream client or its address is
381   *          not available.
382   */
383  @Nullable()
384  public String getDownstreamClientAddress()
385  {
386    return value.getDownstreamClientAddress();
387  }
388
389
390
391  /**
392   * Indicates whether the communication with the communication with the
393   * downstream client is secure (i.e., whether communication between the
394   * client application and the downstream client is safe from interpretation or
395   * undetectable alteration by a third party observer or interceptor).
396   *
397   *
398   * @return  {@code Boolean.TRUE} if communication with the downstream client
399   *          is secure, {@code Boolean.FALSE} if it is not secure, or
400   *          {@code null} if there is no downstream client or it is not known
401   *          whether the communication is secure.
402   */
403  @Nullable()
404  public Boolean downstreamClientSecure()
405  {
406    return value.downstreamClientSecure();
407  }
408
409
410
411  /**
412   * Retrieves a string that identifies the client application that created this
413   * intermediate client request value.
414   *
415   * @return  A string that may be used to identify the client application that
416   *          created this intermediate client request value.
417   */
418  @Nullable()
419  public String getClientName()
420  {
421    return value.getClientName();
422  }
423
424
425
426  /**
427   * Retrieves a string that may be used to identify the session in the client
428   * application.
429   *
430   * @return  A string that may be used to identify the session in the client
431   *          application, or {@code null} if there is none.
432   */
433  @Nullable()
434  public String getClientSessionID()
435  {
436    return value.getClientSessionID();
437  }
438
439
440
441  /**
442   * Retrieves a string that may be used to identify the request in the client
443   * application.
444   *
445   * @return  A string that may be used to identify the request in the client
446   *          application, or {@code null} if there is none.
447   */
448  @Nullable()
449  public String getClientRequestID()
450  {
451    return value.getClientRequestID();
452  }
453
454
455
456  /**
457   * {@inheritDoc}
458   */
459  @Override()
460  @NotNull()
461  public String getControlName()
462  {
463    return INFO_CONTROL_NAME_INTERMEDIATE_CLIENT_REQUEST.get();
464  }
465
466
467
468  /**
469   * Retrieves a representation of this intermediate client request control as a
470   * JSON object.  The JSON object uses the following fields:
471   * <UL>
472   *   <LI>
473   *     {@code oid} -- A mandatory string field whose value is the object
474   *     identifier for this control.  For the intermediate client request
475   *     control, the OID is "1.3.6.1.4.1.30221.2.5.2".
476   *   </LI>
477   *   <LI>
478   *     {@code control-name} -- An optional string field whose value is a
479   *     human-readable name for this control.  This field is only intended for
480   *     descriptive purposes, and when decoding a control, the {@code oid}
481   *     field should be used to identify the type of control.
482   *   </LI>
483   *   <LI>
484   *     {@code criticality} -- A mandatory Boolean field used to indicate
485   *     whether this control is considered critical.
486   *   </LI>
487   *   <LI>
488   *     {@code value-base64} -- An optional string field whose value is a
489   *     base64-encoded representation of the raw value for this intermediate
490   *     client request control.  Exactly one of the {@code value-base64} and
491   *     {@code value-json} fields must be present.
492   *   </LI>
493   *   <LI>
494   *     {@code value-json} -- An optional JSON object field whose value is a
495   *     user-friendly representation of the value for this intermediate client
496   *     request control.  Exactly one of the {@code value-base64} and
497   *     {@code value-json} fields must be present, and if the
498   *     {@code value-json} field is used, then it will use the following
499   *     fields:
500   *     <UL>
501   *       <LI>
502   *         {@code downstream-request} -- An optional JSON object field whose
503   *         content represents a downstream request value.  If present, the
504   *         fields of this object are the same as the fields that may be
505   *         included in the top-level {@code value-json} object (optionally
506   *         including a nested {@code downstream-request} field, if
507   *         appropriate).
508   *       </LI>
509   *       <LI>
510   *         {@code downstream-client-address} -- An optional string field whose
511   *         value is the address of the immediate client from which the request
512   *         was received.
513   *       </LI>
514   *       <LI>
515   *         {@code downstream-client-secure} -- An optional Boolean field that
516   *         indicates whether communication with the immediate client is using
517   *         a secure channel.
518   *       </LI>
519   *       <LI>
520   *         {@code client-identity} -- An optional string field whose value is
521   *         the authorization identity of a user as whom the operation should
522   *         be authorized.
523   *       </LI>
524   *       <LI>
525   *         {@code client-name} -- An optional string field whose value is the
526   *         name of the client application that generated this control.
527   *       </LI>
528   *       <LI>
529   *         {@code client-session-id} -- An optional string field whose value
530   *         is an identifier that the client is using to reference the current
531   *         communication session with a downstream client.
532   *       </LI>
533   *       <LI>
534   *         {@code client-request-id} -- An optional string field whose value
535   *         is an identifier that the client is using to reference this request
536   *         from a downstream client.
537   *       </LI>
538   *     </UL>
539   *   </LI>
540   * </UL>
541   *
542   * @return  A JSON object that contains a representation of this control.
543   */
544  @Override()
545  @NotNull()
546  public JSONObject toJSONControl()
547  {
548    return new JSONObject(
549         new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID,
550              INTERMEDIATE_CLIENT_REQUEST_OID),
551         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME,
552              INFO_CONTROL_NAME_INTERMEDIATE_CLIENT_REQUEST.get()),
553         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY,
554              isCritical()),
555         new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON,
556              encodeRequestValueJSON(value)));
557  }
558
559
560
561  /**
562   * Encodes the provided intermediate client request value to a JSON object.
563   *
564   * @param  value  The intermediate client request value to be encoded.  It
565   *                must not be {@code null}.
566   *
567   * @return  The JSON object containing the encoded intermediate client
568   *          request value.
569   */
570  @NotNull()
571  private static JSONObject encodeRequestValueJSON(
572               @NotNull final IntermediateClientRequestValue value)
573  {
574    final Map<String,JSONValue> fields = new LinkedHashMap<>();
575
576    final IntermediateClientRequestValue downstreamRequest =
577         value.getDownstreamRequest();
578    if (downstreamRequest != null)
579    {
580      fields.put(JSON_FIELD_DOWNSTREAM_REQUEST,
581           encodeRequestValueJSON(downstreamRequest));
582    }
583
584    final String downstreamClientAddress = value.getDownstreamClientAddress();
585    if (downstreamClientAddress != null)
586    {
587      fields.put(JSON_FIELD_DOWNSTREAM_CLIENT_ADDRESS,
588           new JSONString(downstreamClientAddress));
589    }
590
591    final Boolean downstreamClientSecure = value.downstreamClientSecure();
592    if (downstreamClientSecure != null)
593    {
594      fields.put(JSON_FIELD_DOWNSTREAM_CLIENT_SECURE,
595           new JSONBoolean(downstreamClientSecure));
596    }
597
598    final String clientIdentity = value.getClientIdentity();
599    if (clientIdentity != null)
600    {
601      fields.put(JSON_FIELD_CLIENT_IDENTITY, new JSONString(clientIdentity));
602    }
603
604    final String clientName = value.getClientName();
605    if (clientName != null)
606    {
607      fields.put(JSON_FIELD_CLIENT_NAME, new JSONString(clientName));
608    }
609
610    final String clientSessionID = value.getClientSessionID();
611    if (clientSessionID != null)
612    {
613      fields.put(JSON_FIELD_CLIENT_SESSION_ID, new JSONString(clientSessionID));
614    }
615
616    final String clientRequestID = value.getClientRequestID();
617    if (clientRequestID != null)
618    {
619      fields.put(JSON_FIELD_CLIENT_REQUEST_ID, new JSONString(clientRequestID));
620    }
621
622    return new JSONObject(fields);
623  }
624
625
626
627  /**
628   * Attempts to decode the provided object as a JSON representation of an
629   * intermediate client request control.
630   *
631   * @param  controlObject  The JSON object to be decoded.  It must not be
632   *                        {@code null}.
633   * @param  strict         Indicates whether to use strict mode when decoding
634   *                        the provided JSON object.  If this is {@code true},
635   *                        then this method will throw an exception if the
636   *                        provided JSON object contains any unrecognized
637   *                        fields.  If this is {@code false}, then unrecognized
638   *                        fields will be ignored.
639   *
640   * @return  The intermediate client request control that was decoded from
641   *          the provided JSON object.
642   *
643   * @throws  LDAPException  If the provided JSON object cannot be parsed as a
644   *                         valid intermediate client request control.
645   */
646  @NotNull()
647  public static IntermediateClientRequestControl decodeJSONControl(
648              @NotNull final JSONObject controlObject,
649              final boolean strict)
650         throws LDAPException
651  {
652    final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper(
653         controlObject, strict, true, true);
654
655    final ASN1OctetString rawValue = jsonControl.getRawValue();
656    if (rawValue != null)
657    {
658      return new IntermediateClientRequestControl(new Control(
659           jsonControl.getOID(), jsonControl.getCriticality(), rawValue));
660    }
661
662
663    final IntermediateClientRequestValue value =
664         decodeIntermediateClientRequestValueJSON(controlObject,
665              jsonControl.getValueObject(), false, strict);
666    return new IntermediateClientRequestControl(jsonControl.getCriticality(),
667         value);
668  }
669
670
671
672  /**
673   * Decodes the provided JSON Object as an intermediate client request value.
674   *
675   * @param  controlObject        The JSON object that represents the entire
676   *                              intermediate client request control.  It must
677   *                              not be {@code null}.
678   * @param  valueObject          The intermediate client request value to
679   *                              decode.  It must not be {@code null}.
680   * @param  isDownstreamRequest  Indicates whether the provided JSON object
681   *                              represents a downstream request.
682   * @param  strict               Indicates whether to use strict mode when
683   *                              decoding the provided JSON object.  If this is
684   *                              {@code true}, then this method will throw an
685   *                              exception if the provided JSON object contains
686   *                              any unrecognized fields.  If this is
687   *                              {@code false}, then unrecognized fields will
688   *                              be ignored.
689   *
690   * @return  The intermediate client request value that was decoded.
691   *
692   * @throws  LDAPException  If the provided JSON object cannot be decoded as an
693   *                         intermediate client request value.
694   */
695  @NotNull()
696  private static IntermediateClientRequestValue
697               decodeIntermediateClientRequestValueJSON(
698                    @NotNull final JSONObject controlObject,
699                    @NotNull final JSONObject valueObject,
700                    final boolean isDownstreamRequest,
701                    final boolean strict)
702          throws LDAPException
703  {
704    final IntermediateClientRequestValue downstreamRequest;
705    final JSONObject downstreamRequestObject =
706         valueObject.getFieldAsObject(JSON_FIELD_DOWNSTREAM_REQUEST);
707    if (downstreamRequestObject == null)
708    {
709      downstreamRequest = null;
710    }
711    else
712    {
713      downstreamRequest = decodeIntermediateClientRequestValueJSON(
714           controlObject, downstreamRequestObject, true, strict);
715    }
716
717    final String downstreamClientAddress =
718         valueObject.getFieldAsString(JSON_FIELD_DOWNSTREAM_CLIENT_ADDRESS);
719    final Boolean downstreamClientSecure =
720         valueObject.getFieldAsBoolean(JSON_FIELD_DOWNSTREAM_CLIENT_SECURE);
721    final String clientIdentity =
722         valueObject.getFieldAsString(JSON_FIELD_CLIENT_IDENTITY);
723    final String clientName =
724         valueObject.getFieldAsString(JSON_FIELD_CLIENT_NAME);
725    final String clientSessionID =
726         valueObject.getFieldAsString(JSON_FIELD_CLIENT_SESSION_ID);
727    final String clientRequestID =
728         valueObject.getFieldAsString(JSON_FIELD_CLIENT_REQUEST_ID);
729
730
731
732    if (strict)
733    {
734      final List<String> unrecognizedFields =
735           JSONControlDecodeHelper.getControlObjectUnexpectedFields(
736                valueObject, JSON_FIELD_DOWNSTREAM_REQUEST,
737                JSON_FIELD_DOWNSTREAM_CLIENT_ADDRESS,
738                JSON_FIELD_DOWNSTREAM_CLIENT_SECURE,
739                JSON_FIELD_CLIENT_IDENTITY,
740                JSON_FIELD_CLIENT_NAME,
741                JSON_FIELD_CLIENT_SESSION_ID,
742                JSON_FIELD_CLIENT_REQUEST_ID);
743      if (! unrecognizedFields.isEmpty())
744      {
745        if (isDownstreamRequest)
746        {
747          throw new LDAPException(ResultCode.DECODING_ERROR,
748               ERR_INTERMEDIATE_CLIENT_REQUEST_JSON_DS_VALUE_UNRECOGNIZED_FIELD.
749                    get(controlObject.toSingleLineString(),
750                         unrecognizedFields.get(0)));
751        }
752        else
753        {
754          throw new LDAPException(ResultCode.DECODING_ERROR,
755               ERR_INTERMEDIATE_CLIENT_REQUEST_JSON_VALUE_UNRECOGNIZED_FIELD.
756                    get(controlObject.toSingleLineString(),
757                         unrecognizedFields.get(0)));
758        }
759      }
760    }
761
762
763    return new IntermediateClientRequestValue(downstreamRequest,
764         downstreamClientAddress, downstreamClientSecure, clientIdentity,
765         clientName, clientSessionID, clientRequestID);
766  }
767
768
769
770  /**
771   * {@inheritDoc}
772   */
773  @Override()
774  public void toString(@NotNull final StringBuilder buffer)
775  {
776    buffer.append("IntermediateClientRequestControl(isCritical=");
777    buffer.append(isCritical());
778    buffer.append(", value=");
779    value.toString(buffer);
780    buffer.append(')');
781  }
782}