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.ArrayList;
041import java.util.Collection;
042import java.util.LinkedHashMap;
043import java.util.List;
044import java.util.Map;
045
046import com.unboundid.asn1.ASN1Boolean;
047import com.unboundid.asn1.ASN1Element;
048import com.unboundid.asn1.ASN1Exception;
049import com.unboundid.asn1.ASN1OctetString;
050import com.unboundid.asn1.ASN1Sequence;
051import com.unboundid.ldap.sdk.Attribute;
052import com.unboundid.ldap.sdk.BindResult;
053import com.unboundid.ldap.sdk.Control;
054import com.unboundid.ldap.sdk.DecodeableControl;
055import com.unboundid.ldap.sdk.JSONControlDecodeHelper;
056import com.unboundid.ldap.sdk.LDAPException;
057import com.unboundid.ldap.sdk.ReadOnlyEntry;
058import com.unboundid.ldap.sdk.ResultCode;
059import com.unboundid.util.Debug;
060import com.unboundid.util.NotMutable;
061import com.unboundid.util.NotNull;
062import com.unboundid.util.Nullable;
063import com.unboundid.util.StaticUtils;
064import com.unboundid.util.ThreadSafety;
065import com.unboundid.util.ThreadSafetyLevel;
066import com.unboundid.util.json.JSONArray;
067import com.unboundid.util.json.JSONBoolean;
068import com.unboundid.util.json.JSONField;
069import com.unboundid.util.json.JSONObject;
070import com.unboundid.util.json.JSONString;
071import com.unboundid.util.json.JSONValue;
072
073import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
074
075
076
077/**
078 * This class provides an implementation of an LDAP control that may be included
079 * in a bind response to provide information about the authenticated and/or
080 * authorized user.
081 * <BR>
082 * <BLOCKQUOTE>
083 *   <B>NOTE:</B>  This class, and other classes within the
084 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
085 *   supported for use against Ping Identity, UnboundID, and
086 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
087 *   for proprietary functionality or for external specifications that are not
088 *   considered stable or mature enough to be guaranteed to work in an
089 *   interoperable way with other types of LDAP servers.
090 * </BLOCKQUOTE>
091 * <BR>
092 * The value of this control will be encoded as follows:
093 * <PRE>
094 *   GetAuthorizationEntryResponse ::= SEQUENCE {
095 *     isAuthenticated     [0] BOOLEAN,
096 *     identitiesMatch     [1] BOOLEAN,
097 *     authNEntry          [2] AuthEntry OPTIONAL,
098 *     authZEntry          [3] AuthEntry OPTIONAL }
099 *
100 *   AuthEntry ::= SEQUENCE {
101 *     authID         [0] AuthzId OPTIONAL,
102 *     authDN         [1] LDAPDN,
103 *     attributes     [2] PartialAttributeList }
104 * </PRE>
105 * <BR><BR>
106 * See the documentation for the {@link GetAuthorizationEntryRequestControl}
107 * class for more information and an example demonstrating the use of these
108 * controls.
109 */
110@NotMutable()
111@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
112public final class GetAuthorizationEntryResponseControl
113       extends Control
114       implements DecodeableControl
115{
116  /**
117   * The OID (1.3.6.1.4.1.30221.2.5.6) for the get authorization entry response
118   * control.
119   */
120  @NotNull public static final String GET_AUTHORIZATION_ENTRY_RESPONSE_OID =
121       "1.3.6.1.4.1.30221.2.5.6";
122
123
124
125  /**
126   * The BER type for the {@code isAuthenticated} element.
127   */
128  private static final byte TYPE_IS_AUTHENTICATED = (byte) 0x80;
129
130
131
132  /**
133   * The BER type for the {@code identitiesMatch} element.
134   */
135  private static final byte TYPE_IDENTITIES_MATCH = (byte) 0x81;
136
137
138
139  /**
140   * The BER type for the {@code authNEntry} element.
141   */
142  private static final byte TYPE_AUTHN_ENTRY = (byte) 0xA2;
143
144
145
146  /**
147   * The BER type for the {@code authZEntry} element.
148   */
149  private static final byte TYPE_AUTHZ_ENTRY = (byte) 0xA3;
150
151
152
153  /**
154   * The BER type for the {@code authID} element.
155   */
156  private static final byte TYPE_AUTHID = (byte) 0x80;
157
158
159
160  /**
161   * The BER type for the {@code authDN} element.
162   */
163  private static final byte TYPE_AUTHDN = (byte) 0x81;
164
165
166
167  /**
168   * The BER type for the {@code attributesDN} element.
169   */
170  private static final byte TYPE_ATTRIBUTES= (byte) 0xA2;
171
172
173
174  /**
175   * The name of the field used to hold the authentication entry in the JSON
176   * representation of this control.
177   */
178  @NotNull private static final String JSON_FIELD_AUTHENTICATION_ENTRY =
179       "authentication-entry";
180
181
182
183  /**
184   * The name of the field used to hold the authentication identity in the JSON
185   * representation of this control.
186   */
187  @NotNull private static final String JSON_FIELD_AUTHENTICATION_ID =
188       "authentication-id";
189
190
191
192  /**
193   * The name of the field used to hold the authorization entry in the JSON
194   * representation of this control.
195   */
196  @NotNull private static final String JSON_FIELD_AUTHORIZATION_ENTRY =
197       "authorization-entry";
198
199
200
201  /**
202   * The name of the field used to hold the authorization identity in the JSON
203   * representation of this control.
204   */
205  @NotNull private static final String JSON_FIELD_AUTHORIZATION_ID =
206       "authorization-id";
207
208
209
210  /**
211   * The name of the field used to hold the DN of an entry in the JSON
212   * representation of this control.
213   */
214  @NotNull private static final String JSON_FIELD_ENTRY_DN = "_dn";
215
216
217
218  /**
219   * The name of the field used to indicate whether authentication and
220   * authorization identities match in the JSON representation of this control.
221   */
222  @NotNull private static final String JSON_FIELD_IDENTITIES_MATCH =
223       "identities-match";
224
225
226
227  /**
228   * The name of the field used to indicate whether the client is authenticated
229   * in the JSON representation of this control.
230   */
231  @NotNull private static final String JSON_FIELD_IS_AUTHENTICATED =
232       "is-authenticated";
233
234
235
236  /**
237   * The serial version UID for this serializable class.
238   */
239  private static final long serialVersionUID = -5443107150740697226L;
240
241
242
243  // Indicates whether the authentication and authorization identities are the
244  // same.
245  private final boolean identitiesMatch;
246
247  // Indicates whether the client is authenticated.
248  private final boolean isAuthenticated;
249
250  // The entry for the authentication identity, if available.
251  @Nullable private final ReadOnlyEntry authNEntry;
252
253  // The entry for the authorization identity, if available.
254  @Nullable private final ReadOnlyEntry authZEntry;
255
256  // The authID for the authentication identity, if available.
257  @Nullable private final String authNID;
258
259  // The authID for the authorization identity, if available.
260  @Nullable private final String authZID;
261
262
263
264  /**
265   * Creates a new empty control instance that is intended to be used only for
266   * decoding controls via the {@code DecodeableControl} interface.
267   */
268  GetAuthorizationEntryResponseControl()
269  {
270    isAuthenticated = false;
271    identitiesMatch = true;
272    authNEntry      = null;
273    authNID         = null;
274    authZEntry      = null;
275    authZID         = null;
276  }
277
278
279
280  /**
281   * Creates a new get authorization entry response control with the provided
282   * information.
283   *
284   * @param  isAuthenticated  Indicates whether the client is authenticated.
285   * @param  identitiesMatch  Indicates whether the authentication identity is
286   *                          the same as the authorization identity.
287   * @param  authNID          The string that may be used to reference the
288   *                          authentication identity.  It may be {@code null}
289   *                          if information about the authentication identity
290   *                          is not to be included, or if the identifier should
291   *                          be derived from the DN.
292   * @param  authNEntry       The entry for the authentication identity.  It may
293   *                          be {@code null} if the information about the
294   *                          authentication identity is not to be included.
295   * @param  authZID          The string that may be used to reference the
296   *                          authorization identity.  It may be {@code null}
297   *                          if information about the authentication identity
298   *                          is not to be included, if the identifier should
299   *                          be derived from the DN, or if the authentication
300   *                          and authorization identities are the same.
301   * @param  authZEntry       The entry for the authentication identity.  It may
302   *                          be {@code null} if the information about the
303   *                          authentication identity is not to be included, or
304   *                          if the authentication and authorization identities
305   *                          are the same.
306   */
307  public GetAuthorizationEntryResponseControl(final boolean isAuthenticated,
308              final boolean identitiesMatch, @Nullable final String authNID,
309              @Nullable final ReadOnlyEntry authNEntry,
310              @Nullable final String authZID,
311              @Nullable final ReadOnlyEntry authZEntry)
312  {
313    super(GET_AUTHORIZATION_ENTRY_RESPONSE_OID, false,
314          encodeValue(isAuthenticated, identitiesMatch, authNID, authNEntry,
315                      authZID, authZEntry));
316
317    this.isAuthenticated = isAuthenticated;
318    this.identitiesMatch = identitiesMatch;
319    this.authNID         = authNID;
320    this.authNEntry      = authNEntry;
321    this.authZID         = authZID;
322    this.authZEntry      = authZEntry;
323  }
324
325
326
327  /**
328   * Creates a new get authorization entry response control with the provided
329   * information.
330   *
331   * @param  oid         The OID for the control.
332   * @param  isCritical  Indicates whether the control should be marked
333   *                     critical.
334   * @param  value       The encoded value for the control.  This may be
335   *                     {@code null} if no value was provided.
336   *
337   * @throws  LDAPException  If the provided control cannot be decoded as a get
338   *                         authorization entry response control.
339   */
340  public GetAuthorizationEntryResponseControl(@NotNull final String oid,
341              final boolean isCritical,
342              @Nullable final ASN1OctetString value)
343         throws LDAPException
344  {
345    super(oid, isCritical,  value);
346
347    if (value == null)
348    {
349      throw new LDAPException(ResultCode.DECODING_ERROR,
350           ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_NO_VALUE.get());
351    }
352
353    try
354    {
355      boolean       isAuth   = false;
356      boolean       idsMatch = false;
357      String        nID      = null;
358      String        zID      = null;
359      ReadOnlyEntry nEntry   = null;
360      ReadOnlyEntry zEntry   = null;
361
362      final ASN1Element valElement = ASN1Element.decode(value.getValue());
363      for (final ASN1Element e :
364           ASN1Sequence.decodeAsSequence(valElement).elements())
365      {
366        switch (e.getType())
367        {
368          case TYPE_IS_AUTHENTICATED:
369            isAuth = ASN1Boolean.decodeAsBoolean(e).booleanValue();
370            break;
371          case TYPE_IDENTITIES_MATCH:
372            idsMatch = ASN1Boolean.decodeAsBoolean(e).booleanValue();
373            break;
374          case TYPE_AUTHN_ENTRY:
375            final Object[] nObjects = decodeAuthEntry(e);
376            nID = (String) nObjects[0];
377            nEntry = (ReadOnlyEntry) nObjects[1];
378            break;
379          case TYPE_AUTHZ_ENTRY:
380            final Object[] zObjects = decodeAuthEntry(e);
381            zID = (String) zObjects[0];
382            zEntry = (ReadOnlyEntry) zObjects[1];
383            break;
384          default:
385            throw new LDAPException(ResultCode.DECODING_ERROR,
386                 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_VALUE_TYPE.get(
387                      StaticUtils.toHex(e.getType())));
388        }
389      }
390
391      isAuthenticated = isAuth;
392      identitiesMatch = idsMatch;
393      authNID         = nID;
394      authNEntry      = nEntry;
395      authZID         = zID;
396      authZEntry      = zEntry;
397    }
398    catch (final Exception e)
399    {
400      Debug.debugException(e);
401      throw new LDAPException(ResultCode.DECODING_ERROR,
402           ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_CANNOT_DECODE_VALUE.get(
403                StaticUtils.getExceptionMessage(e)),
404           e);
405    }
406  }
407
408
409
410  /**
411   * {@inheritDoc}
412   */
413  @Override()
414  @NotNull()
415  public GetAuthorizationEntryResponseControl decodeControl(
416              @NotNull final String oid,
417              final boolean isCritical,
418              @Nullable final ASN1OctetString value)
419         throws LDAPException
420  {
421    return new GetAuthorizationEntryResponseControl(oid, isCritical, value);
422  }
423
424
425
426  /**
427   * Extracts a get authorization entry response control from the provided
428   * result.
429   *
430   * @param  result  The result from which to retrieve the get authorization
431   *                 entry response control.
432   *
433   * @return  The get authorization entry response control contained in the
434   *          provided result, or {@code null} if the result did not contain a
435   *          get authorization entry response control.
436   *
437   * @throws  LDAPException  If a problem is encountered while attempting to
438   *                         decode the get authorization entry response control
439   *                         contained in the provided result.
440   */
441  @Nullable()
442  public static GetAuthorizationEntryResponseControl get(
443                     @NotNull final BindResult result)
444         throws LDAPException
445  {
446    final Control c =
447         result.getResponseControl(GET_AUTHORIZATION_ENTRY_RESPONSE_OID);
448    if (c == null)
449    {
450      return null;
451    }
452
453    if (c instanceof GetAuthorizationEntryResponseControl)
454    {
455      return (GetAuthorizationEntryResponseControl) c;
456    }
457    else
458    {
459      return new GetAuthorizationEntryResponseControl(c.getOID(),
460           c.isCritical(), c.getValue());
461    }
462  }
463
464
465
466  /**
467   * Encodes the provided information appropriately for use as the value of this
468   * control.
469   *
470   * @param  isAuthenticated  Indicates whether the client is authenticated.
471   * @param  identitiesMatch  Indicates whether the authentication identity is
472   *                          the same as the authorization identity.
473   * @param  authNID          The string that may be used to reference the
474   *                          authentication identity.  It may be {@code null}
475   *                          if information about the authentication identity
476   *                          is not to be included, or if the identifier should
477   *                          be derived from the DN.
478   * @param  authNEntry       The entry for the authentication identity.  It may
479   *                          be {@code null} if the information about the
480   *                          authentication identity is not to be included.
481   * @param  authZID          The string that may be used to reference the
482   *                          authorization identity.  It may be {@code null}
483   *                          if information about the authentication identity
484   *                          is not to be included, if the identifier should
485   *                          be derived from the DN, or if the authentication
486   *                          and authorization identities are the same.
487   * @param  authZEntry       The entry for the authentication identity.  It may
488   *                          be {@code null} if the information about the
489   *                          authentication identity is not to be included, or
490   *                          if the authentication and authorization identities
491   *                          are the same.
492   *
493   * @return  The ASN.1 octet string suitable for use as the control value.
494   */
495  @NotNull()
496  private static ASN1OctetString encodeValue(final boolean isAuthenticated,
497                      final boolean identitiesMatch,
498                      @Nullable final String authNID,
499                      @Nullable final ReadOnlyEntry authNEntry,
500                      @Nullable final String authZID,
501                      @Nullable final ReadOnlyEntry authZEntry)
502  {
503    final ArrayList<ASN1Element> elements = new ArrayList<>(4);
504    elements.add(new ASN1Boolean(TYPE_IS_AUTHENTICATED, isAuthenticated));
505    elements.add(new ASN1Boolean(TYPE_IDENTITIES_MATCH, identitiesMatch));
506
507    if (authNEntry != null)
508    {
509      elements.add(encodeAuthEntry(TYPE_AUTHN_ENTRY, authNID, authNEntry));
510    }
511
512    if (authZEntry != null)
513    {
514      elements.add(encodeAuthEntry(TYPE_AUTHZ_ENTRY, authZID, authZEntry));
515    }
516
517    return new ASN1OctetString(new ASN1Sequence(elements).encode());
518  }
519
520
521
522  /**
523   * Encodes the provided information as appropriate for an auth entry.
524   *
525   * @param  type       The BER type to use for the element.
526   * @param  authID     The authID to be encoded, if available.
527   * @param  authEntry  The entry to be encoded.
528   *
529   * @return  The ASN.1 sequence containing the encoded auth entry.
530   */
531  @NotNull()
532  private static ASN1Sequence encodeAuthEntry(final byte type,
533                      @Nullable final String authID,
534                      @NotNull final ReadOnlyEntry authEntry)
535  {
536    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
537
538    if (authID != null)
539    {
540      elements.add(new ASN1OctetString(TYPE_AUTHID, authID));
541    }
542
543    elements.add(new ASN1OctetString(TYPE_AUTHDN, authEntry.getDN()));
544
545    final Collection<Attribute> attributes = authEntry.getAttributes();
546    final ArrayList<ASN1Element> attrElements =
547         new ArrayList<>(attributes.size());
548    for (final Attribute a : attributes)
549    {
550      attrElements.add(a.encode());
551    }
552    elements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements));
553
554    return new ASN1Sequence(type, elements);
555  }
556
557
558
559  /**
560   * Decodes the provided ASN.1 element into an array of auth entry elements.
561   * The first element of the array will be the auth ID, and the second element
562   * will be the read-only entry.
563   *
564   * @param  element  The element to decode.
565   *
566   * @return  The decoded array of elements.
567   *
568   * @throws  ASN1Exception  If a problem occurs while performing ASN.1 parsing.
569   *
570   * @throws  LDAPException  If a problem occurs while performing LDAP parsing.
571   */
572  @NotNull()
573  private static Object[] decodeAuthEntry(@NotNull final ASN1Element element)
574          throws ASN1Exception, LDAPException
575  {
576    String authID = null;
577    String authDN = null;
578    final ArrayList<Attribute> attrs = new ArrayList<>(20);
579
580    for (final ASN1Element e :
581         ASN1Sequence.decodeAsSequence(element).elements())
582    {
583      switch (e.getType())
584      {
585        case TYPE_AUTHID:
586          authID = ASN1OctetString.decodeAsOctetString(e).stringValue();
587          break;
588        case TYPE_AUTHDN:
589          authDN = ASN1OctetString.decodeAsOctetString(e).stringValue();
590          break;
591        case TYPE_ATTRIBUTES:
592          for (final ASN1Element ae :
593               ASN1Sequence.decodeAsSequence(e).elements())
594          {
595            attrs.add(Attribute.decode(ASN1Sequence.decodeAsSequence(ae)));
596          }
597          break;
598        default:
599          throw new LDAPException(ResultCode.DECODING_ERROR,
600               ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_ENTRY_TYPE.get(
601                    StaticUtils.toHex(e.getType())));
602      }
603    }
604
605    return new Object[] { authID, new ReadOnlyEntry(authDN, attrs) };
606  }
607
608
609
610  /**
611   * Indicates whether the client is authenticated.
612   *
613   * @return  {@code true} if the client is authenticated, or {@code false} if
614   *          not.
615   */
616  public boolean isAuthenticated()
617  {
618    return isAuthenticated;
619  }
620
621
622
623  /**
624   * Indicates whether the authentication identity and the authorization
625   * identity reference the same user.
626   *
627   * @return  {@code true} if both the authentication identity and the
628   *          authorization identity reference the same user, or {@code false}
629   *          if not.
630   */
631  public boolean identitiesMatch()
632  {
633    return identitiesMatch;
634  }
635
636
637
638  /**
639   * Retrieves the identifier that may be used to reference the authentication
640   * identity in the directory server, if it is available.
641   *
642   * @return  The identifier that may be used to reference the authentication
643   *          identity in the directory server, or {@code null} if it is not
644   *          available.
645   */
646  @Nullable()
647  public String getAuthNID()
648  {
649    if ((authNID == null) && identitiesMatch)
650    {
651      return authZID;
652    }
653
654    return authNID;
655  }
656
657
658
659  /**
660   * Retrieves the entry for the user specified as the authentication identity,
661   * if it is available.
662   *
663   * @return  The entry for the user specified as the authentication identity,
664   *          or {@code null} if it is not available.
665   */
666  @Nullable()
667  public ReadOnlyEntry getAuthNEntry()
668  {
669    if ((authNEntry == null) && identitiesMatch)
670    {
671      return authZEntry;
672    }
673
674    return authNEntry;
675  }
676
677
678
679  /**
680   * Retrieves the identifier that may be used to reference the authorization
681   * identity in the directory server, if it is available.
682   *
683   * @return  The identifier that may be used to reference the authorization
684   *          identity in the directory server, or {@code null} if it is not
685   *          available.
686   */
687  @Nullable()
688  public String getAuthZID()
689  {
690    if ((authZID == null) && identitiesMatch)
691    {
692      return authNID;
693    }
694
695    return authZID;
696  }
697
698
699
700  /**
701   * Retrieves the entry for the user specified as the authorization identity,
702   * if it is available.
703   *
704   * @return  The entry for the user specified as the authorization identity,
705   *          or {@code null} if it is not available.
706   */
707  @Nullable()
708  public ReadOnlyEntry getAuthZEntry()
709  {
710    if ((authZEntry == null) && identitiesMatch)
711    {
712      return authNEntry;
713    }
714
715    return authZEntry;
716  }
717
718
719
720  /**
721   * {@inheritDoc}
722   */
723  @Override()
724  @NotNull()
725  public String getControlName()
726  {
727    return INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get();
728  }
729
730
731
732  /**
733   * Retrieves a representation of this get authorization entry response control
734   * as a JSON object.  The JSON object uses the following fields:
735   * <UL>
736   *   <LI>
737   *     {@code oid} -- A mandatory string field whose value is the object
738   *     identifier for this control.  For the get authorization entry response
739   *     control, the OID is "1.3.6.1.4.1.30221.2.5.6".
740   *   </LI>
741   *   <LI>
742   *     {@code control-name} -- An optional string field whose value is a
743   *     human-readable name for this control.  This field is only intended for
744   *     descriptive purposes, and when decoding a control, the {@code oid}
745   *     field should be used to identify the type of control.
746   *   </LI>
747   *   <LI>
748   *     {@code criticality} -- A mandatory Boolean field used to indicate
749   *     whether this control is considered critical.
750   *   </LI>
751   *   <LI>
752   *     {@code value-base64} -- An optional string field whose value is a
753   *     base64-encoded representation of the raw value for this get
754   *     authorization entry response control.  Exactly one of the
755   *     {@code value-base64} and {@code value-json} fields must be present.
756   *   </LI>
757   *   <LI>
758   *     {@code value-json} -- An optional JSON object field whose value is a
759   *     user-friendly representation of the value for this get authorization
760   *     entry response control.  Exactly one of the {@code value-base64} and
761   *     {@code value-json} fields must be present, and if the
762   *     {@code value-json} field is used, then it will use the following
763   *     fields:
764   *     <UL>
765   *       <LI>
766   *         {@code is-authenticated} -- A Boolean field that indicates whether
767   *         the bind operation resulted in an authenticated or anonymous
768   *         authentication state.
769   *       </LI>
770   *       <LI>
771   *         {@code identities-match} -- A Boolean field that indicates whether
772   *         the resulting authentication identity matches the resulting
773   *         authorization identity.
774   *       </LI>
775   *       <LI>
776   *         {@code authentication-id} -- An optional string field that holds
777   *         the resulting authentication identity.
778   *       </LI>
779   *       <LI>
780   *         {@code authentication-entry} -- An optional JSON object field that
781   *         contains the content of the entry for the resulting authentication
782   *         identity for the operation.  If present, the object will include a
783   *         "{@code _dn}" field whose value is the DN of the entry, and all
784   *         other fields will have a name that is the name of an LDAP attribute
785   *         in the entry and a value that is an array containing the string
786   *         representations of the values for that attribute.
787   *       </LI>
788   *       <LI>
789   *         {@code authorization-id} -- An optional string field that holds
790   *         the resulting authorization identity.
791   *       </LI>
792   *       <LI>
793   *         {@code authorization-entry} -- An optional JSON object field that
794   *         contains the content of the entry for the resulting authorization
795   *         identity for the operation.  If present, the object will include a
796   *         "{@code _dn}" field whose value is the DN of the entry, and all
797   *         other fields will have a name that is the name of an LDAP attribute
798   *         in the entry and a value that is an array containing the string
799   *         representations of the values for that attribute.
800   *       </LI>
801   *     </UL>
802   *   </LI>
803   * </UL>
804   *
805   * @return  A JSON object that contains a representation of this control.
806   */
807  @Override()
808  @NotNull()
809  public JSONObject toJSONControl()
810  {
811    final Map<String,JSONValue> valueFields = new LinkedHashMap<>();
812    valueFields.put(JSON_FIELD_IS_AUTHENTICATED,
813         new JSONBoolean(isAuthenticated));
814    valueFields.put(JSON_FIELD_IDENTITIES_MATCH,
815         new JSONBoolean(identitiesMatch));
816
817    if (authNID != null)
818    {
819      valueFields.put(JSON_FIELD_AUTHENTICATION_ID, new JSONString(authNID));
820    }
821
822    if (authNEntry != null)
823    {
824      valueFields.put(JSON_FIELD_AUTHENTICATION_ENTRY,
825           encodeEntryJSON(authNEntry));
826    }
827
828    if ((authZEntry != null) && (! identitiesMatch))
829    {
830      if (authZID != null)
831      {
832        valueFields.put(JSON_FIELD_AUTHORIZATION_ID, new JSONString(authZID));
833      }
834
835      valueFields.put(JSON_FIELD_AUTHORIZATION_ENTRY,
836           encodeEntryJSON(authZEntry));
837    }
838
839
840    return new JSONObject(
841         new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID,
842              GET_AUTHORIZATION_ENTRY_RESPONSE_OID),
843         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME,
844              INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get()),
845         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY,
846              isCritical()),
847         new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON,
848              new JSONObject(valueFields)));
849  }
850
851
852
853  /**
854   * Retrieves a JSON object containing an encoded representation of the
855   * provided entry.
856   *
857   * @param  entry  The entry to be encoded.  It must not be {@code null}.
858   *
859   * @return  A JSON object containing an encoded representation of the provided
860   *          entry.
861   */
862  @NotNull()
863  private static JSONObject encodeEntryJSON(@NotNull final ReadOnlyEntry entry)
864  {
865    final Map<String,JSONValue> entryFields = new LinkedHashMap<>();
866    entryFields.put(JSON_FIELD_ENTRY_DN, new JSONString(entry.getDN()));
867    for (final Attribute a : entry.getAttributes())
868    {
869      final List<JSONValue> attributeValues = new ArrayList<>(a.size());
870      for (final String v : a.getValues())
871      {
872        attributeValues.add(new JSONString(v));
873      }
874
875      entryFields.put(a.getName(), new JSONArray(attributeValues));
876    }
877
878    return new JSONObject(entryFields);
879  }
880
881
882
883  /**
884   * Attempts to decode the provided object as a JSON representation of a get
885   * authorization entry response control.
886   *
887   * @param  controlObject  The JSON object to be decoded.  It must not be
888   *                        {@code null}.
889   * @param  strict         Indicates whether to use strict mode when decoding
890   *                        the provided JSON object.  If this is {@code true},
891   *                        then this method will throw an exception if the
892   *                        provided JSON object contains any unrecognized
893   *                        fields.  If this is {@code false}, then unrecognized
894   *                        fields will be ignored.
895   *
896   * @return  The get authorization entry response control that was decoded from
897   *          the provided JSON object.
898   *
899   * @throws  LDAPException  If the provided JSON object cannot be parsed as a
900   *                         valid get authorization entry response control.
901   */
902  @NotNull()
903  public static GetAuthorizationEntryResponseControl decodeJSONControl(
904              @NotNull final JSONObject controlObject,
905              final boolean strict)
906         throws LDAPException
907  {
908    final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper(
909         controlObject, strict, true, true);
910
911    final ASN1OctetString rawValue = jsonControl.getRawValue();
912    if (rawValue != null)
913    {
914      return new GetAuthorizationEntryResponseControl(jsonControl.getOID(),
915           jsonControl.getCriticality(), rawValue);
916    }
917
918
919    final JSONObject valueObject = jsonControl.getValueObject();
920
921    final Boolean isAuthenticated =
922         valueObject.getFieldAsBoolean(JSON_FIELD_IS_AUTHENTICATED);
923    if (isAuthenticated == null)
924    {
925      throw new LDAPException(ResultCode.DECODING_ERROR,
926           ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_MISSING_FIELD.get(
927                controlObject.toSingleLineString(),
928                JSON_FIELD_IS_AUTHENTICATED));
929    }
930
931    final Boolean identitiesMatch =
932         valueObject.getFieldAsBoolean(JSON_FIELD_IDENTITIES_MATCH);
933    if (identitiesMatch == null)
934    {
935      throw new LDAPException(ResultCode.DECODING_ERROR,
936           ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_MISSING_FIELD.get(
937                controlObject.toSingleLineString(),
938                JSON_FIELD_IDENTITIES_MATCH));
939    }
940
941    final String authenticationID =
942         valueObject.getFieldAsString(JSON_FIELD_AUTHENTICATION_ID);
943
944    final ReadOnlyEntry authenticationEntry = decodeEntryJSON(controlObject,
945         valueObject, JSON_FIELD_AUTHENTICATION_ENTRY);
946
947    final String authorizationID =
948         valueObject.getFieldAsString(JSON_FIELD_AUTHORIZATION_ID);
949
950    final ReadOnlyEntry authorizationEntry = decodeEntryJSON(controlObject,
951         valueObject, JSON_FIELD_AUTHORIZATION_ENTRY);
952
953
954    if (strict)
955    {
956      final List<String> unrecognizedFields =
957           JSONControlDecodeHelper.getControlObjectUnexpectedFields(
958                valueObject, JSON_FIELD_IS_AUTHENTICATED,
959                JSON_FIELD_IDENTITIES_MATCH, JSON_FIELD_AUTHENTICATION_ID,
960                JSON_FIELD_AUTHENTICATION_ENTRY, JSON_FIELD_AUTHORIZATION_ID,
961                JSON_FIELD_AUTHORIZATION_ENTRY);
962      if (! unrecognizedFields.isEmpty())
963      {
964        throw new LDAPException(ResultCode.DECODING_ERROR,
965             ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_UNRECOGNIZED_FIELD.get(
966                  controlObject.toSingleLineString(),
967                  unrecognizedFields.get(0)));
968      }
969    }
970
971
972    return new GetAuthorizationEntryResponseControl(isAuthenticated,
973         identitiesMatch, authenticationID, authenticationEntry,
974         authorizationID, authorizationEntry);
975  }
976
977
978
979  /**
980   * Retrieves and decodes the entry contained in the specified field of the
981   * provided value object.
982   *
983   * @param  controlObject  A JSON object containing an encoded representation
984   *                        of the entire control being decoded.  It must not
985   *                        be {@code null}.
986   * @param  valueObject    A JSON object containing an encoded representation
987   *                        of the control value.  It must not be {@code null}.
988   * @param  entryField     The name of the field containing the entry to be
989   *                        decoded.  It must not be {@code null}.
990   *
991   * @return  The entry decoded from the specified field in the value object, or
992   *          {@code null} if that field is not present in the value object.
993   *
994   * @throws  LDAPException  If value of the specified field cannot be parsed as
995   *                         an entry.
996   */
997  @Nullable()
998  private static ReadOnlyEntry decodeEntryJSON(
999               @NotNull final JSONObject controlObject,
1000               @NotNull final JSONObject valueObject,
1001               @NotNull final String entryField)
1002          throws LDAPException
1003  {
1004    final JSONObject entryObject = valueObject.getFieldAsObject(entryField);
1005    if (entryObject == null)
1006    {
1007      return null;
1008    }
1009
1010    String dn = null;
1011    final List<Attribute> attributes =
1012         new ArrayList<>(entryObject.getFields().size());
1013    for (final Map.Entry<String,JSONValue> fieldEntry :
1014         entryObject.getFields().entrySet())
1015    {
1016      final String fieldName = fieldEntry.getKey();
1017      final JSONValue fieldValue = fieldEntry.getValue();
1018      if (fieldName.equals(JSON_FIELD_ENTRY_DN))
1019      {
1020        if (fieldValue instanceof JSONString)
1021        {
1022          dn = ((JSONString) fieldValue).stringValue();
1023        }
1024        else
1025        {
1026          throw new LDAPException(ResultCode.DECODING_ERROR,
1027               ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_DN_NOT_STRING.get(
1028                    controlObject.toSingleLineString(), JSON_FIELD_ENTRY_DN,
1029                    entryField));
1030        }
1031      }
1032      else
1033      {
1034        if (fieldValue instanceof JSONArray)
1035        {
1036          final List<JSONValue> jsonValues =
1037               ((JSONArray) fieldValue).getValues();
1038          final List<String> stringValues = new ArrayList<>(jsonValues.size());
1039          for (final JSONValue v : jsonValues)
1040          {
1041            if (v instanceof JSONString)
1042            {
1043              stringValues.add(((JSONString) v).stringValue());
1044            }
1045            else
1046            {
1047              throw new LDAPException(ResultCode.DECODING_ERROR,
1048                   ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_VALUE_NOT_STRING.
1049                        get(controlObject.toSingleLineString(), entryField,
1050                             fieldName));
1051            }
1052          }
1053
1054          attributes.add(new Attribute(fieldName, stringValues));
1055        }
1056        else
1057        {
1058          throw new LDAPException(ResultCode.DECODING_ERROR,
1059               ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_JSON_ATTR_VALUE_NOT_ARRAY.
1060                    get(controlObject.toSingleLineString(), entryField,
1061                         fieldName));
1062        }
1063      }
1064    }
1065
1066    if (dn == null)
1067    {
1068      throw new LDAPException(ResultCode.DECODING_ERROR,
1069           ERR_GET_AUTHORIZATION_ENTRY_REQUEST_JSON_ENTRY_MISSING_DN.get(
1070                controlObject.toSingleLineString(), entryField,
1071                JSON_FIELD_ENTRY_DN));
1072    }
1073
1074    return new ReadOnlyEntry(dn, attributes);
1075  }
1076
1077
1078
1079  /**
1080   * {@inheritDoc}
1081   */
1082  @Override()
1083  public void toString(@NotNull final StringBuilder buffer)
1084  {
1085    buffer.append("GetAuthorizationEntryResponseControl(identitiesMatch=");
1086    buffer.append(identitiesMatch);
1087
1088    if (authNID != null)
1089    {
1090      buffer.append(", authNID='");
1091      buffer.append(authNID);
1092      buffer.append('\'');
1093    }
1094
1095    if (authNEntry != null)
1096    {
1097      buffer.append(", authNEntry=");
1098      authNEntry.toString(buffer);
1099    }
1100
1101    if (authZID != null)
1102    {
1103      buffer.append(", authZID='");
1104      buffer.append(authZID);
1105      buffer.append('\'');
1106    }
1107
1108    if (authZEntry != null)
1109    {
1110      buffer.append(", authZEntry=");
1111      authZEntry.toString(buffer);
1112    }
1113
1114    buffer.append(')');
1115  }
1116}