001/*
002 * Copyright 2008-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-2022 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-2022 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;
042
043import com.unboundid.asn1.ASN1Boolean;
044import com.unboundid.asn1.ASN1Element;
045import com.unboundid.asn1.ASN1Exception;
046import com.unboundid.asn1.ASN1OctetString;
047import com.unboundid.asn1.ASN1Sequence;
048import com.unboundid.ldap.sdk.Attribute;
049import com.unboundid.ldap.sdk.BindResult;
050import com.unboundid.ldap.sdk.Control;
051import com.unboundid.ldap.sdk.DecodeableControl;
052import com.unboundid.ldap.sdk.LDAPException;
053import com.unboundid.ldap.sdk.ReadOnlyEntry;
054import com.unboundid.ldap.sdk.ResultCode;
055import com.unboundid.util.Debug;
056import com.unboundid.util.NotMutable;
057import com.unboundid.util.NotNull;
058import com.unboundid.util.Nullable;
059import com.unboundid.util.StaticUtils;
060import com.unboundid.util.ThreadSafety;
061import com.unboundid.util.ThreadSafetyLevel;
062
063import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
064
065
066
067/**
068 * This class provides an implementation of an LDAP control that may be included
069 * in a bind response to provide information about the authenticated and/or
070 * authorized user.
071 * <BR>
072 * <BLOCKQUOTE>
073 *   <B>NOTE:</B>  This class, and other classes within the
074 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
075 *   supported for use against Ping Identity, UnboundID, and
076 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
077 *   for proprietary functionality or for external specifications that are not
078 *   considered stable or mature enough to be guaranteed to work in an
079 *   interoperable way with other types of LDAP servers.
080 * </BLOCKQUOTE>
081 * <BR>
082 * The value of this control will be encoded as follows:
083 * <PRE>
084 *   GetAuthorizationEntryResponse ::= SEQUENCE {
085 *     isAuthenticated     [0] BOOLEAN,
086 *     identitiesMatch     [1] BOOLEAN,
087 *     authNEntry          [2] AuthEntry OPTIONAL,
088 *     authZEntry          [3] AuthEntry OPTIONAL }
089 *
090 *   AuthEntry ::= SEQUENCE {
091 *     authID         [0] AuthzId OPTIONAL,
092 *     authDN         [1] LDAPDN,
093 *     attributes     [2] PartialAttributeList }
094 * </PRE>
095 * <BR><BR>
096 * See the documentation for the {@link GetAuthorizationEntryRequestControl}
097 * class for more information and an example demonstrating the use of these
098 * controls.
099 */
100@NotMutable()
101@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
102public final class GetAuthorizationEntryResponseControl
103       extends Control
104       implements DecodeableControl
105{
106  /**
107   * The OID (1.3.6.1.4.1.30221.2.5.6) for the get authorization entry response
108   * control.
109   */
110  @NotNull public static final String GET_AUTHORIZATION_ENTRY_RESPONSE_OID =
111       "1.3.6.1.4.1.30221.2.5.6";
112
113
114
115  /**
116   * The BER type for the {@code isAuthenticated} element.
117   */
118  private static final byte TYPE_IS_AUTHENTICATED = (byte) 0x80;
119
120
121
122  /**
123   * The BER type for the {@code identitiesMatch} element.
124   */
125  private static final byte TYPE_IDENTITIES_MATCH = (byte) 0x81;
126
127
128
129  /**
130   * The BER type for the {@code authNEntry} element.
131   */
132  private static final byte TYPE_AUTHN_ENTRY = (byte) 0xA2;
133
134
135
136  /**
137   * The BER type for the {@code authZEntry} element.
138   */
139  private static final byte TYPE_AUTHZ_ENTRY = (byte) 0xA3;
140
141
142
143  /**
144   * The BER type for the {@code authID} element.
145   */
146  private static final byte TYPE_AUTHID = (byte) 0x80;
147
148
149
150  /**
151   * The BER type for the {@code authDN} element.
152   */
153  private static final byte TYPE_AUTHDN = (byte) 0x81;
154
155
156
157  /**
158   * The BER type for the {@code attributesDN} element.
159   */
160  private static final byte TYPE_ATTRIBUTES= (byte) 0xA2;
161
162
163
164  /**
165   * The serial version UID for this serializable class.
166   */
167  private static final long serialVersionUID = -5443107150740697226L;
168
169
170
171  // Indicates whether the authentication and authorization identities are the
172  // same.
173  private final boolean identitiesMatch;
174
175  // Indicates whether the client is authenticated.
176  private final boolean isAuthenticated;
177
178  // The entry for the authentication identity, if available.
179  @Nullable private final ReadOnlyEntry authNEntry;
180
181  // The entry for the authorization identity, if available.
182  @Nullable private final ReadOnlyEntry authZEntry;
183
184  // The authID for the authentication identity, if available.
185  @Nullable private final String authNID;
186
187  // The authID for the authorization identity, if available.
188  @Nullable private final String authZID;
189
190
191
192  /**
193   * Creates a new empty control instance that is intended to be used only for
194   * decoding controls via the {@code DecodeableControl} interface.
195   */
196  GetAuthorizationEntryResponseControl()
197  {
198    isAuthenticated = false;
199    identitiesMatch = true;
200    authNEntry      = null;
201    authNID         = null;
202    authZEntry      = null;
203    authZID         = null;
204  }
205
206
207
208  /**
209   * Creates a new get authorization entry response control with the provided
210   * information.
211   *
212   * @param  isAuthenticated  Indicates whether the client is authenticated.
213   * @param  identitiesMatch  Indicates whether the authentication identity is
214   *                          the same as the authorization identity.
215   * @param  authNID          The string that may be used to reference the
216   *                          authentication identity.  It may be {@code null}
217   *                          if information about the authentication identity
218   *                          is not to be included, or if the identifier should
219   *                          be derived from the DN.
220   * @param  authNEntry       The entry for the authentication identity.  It may
221   *                          be {@code null} if the information about the
222   *                          authentication identity is not to be included.
223   * @param  authZID          The string that may be used to reference the
224   *                          authorization identity.  It may be {@code null}
225   *                          if information about the authentication identity
226   *                          is not to be included, if the identifier should
227   *                          be derived from the DN, or if the authentication
228   *                          and authorization identities are the same.
229   * @param  authZEntry       The entry for the authentication identity.  It may
230   *                          be {@code null} if the information about the
231   *                          authentication identity is not to be included, or
232   *                          if the authentication and authorization identities
233   *                          are the same.
234   */
235  public GetAuthorizationEntryResponseControl(final boolean isAuthenticated,
236              final boolean identitiesMatch, @Nullable final String authNID,
237              @Nullable final ReadOnlyEntry authNEntry,
238              @Nullable final String authZID,
239              @Nullable final ReadOnlyEntry authZEntry)
240  {
241    super(GET_AUTHORIZATION_ENTRY_RESPONSE_OID, false,
242          encodeValue(isAuthenticated, identitiesMatch, authNID, authNEntry,
243                      authZID, authZEntry));
244
245    this.isAuthenticated = isAuthenticated;
246    this.identitiesMatch = identitiesMatch;
247    this.authNID         = authNID;
248    this.authNEntry      = authNEntry;
249    this.authZID         = authZID;
250    this.authZEntry      = authZEntry;
251  }
252
253
254
255  /**
256   * Creates a new get authorization entry response control with the provided
257   * information.
258   *
259   * @param  oid         The OID for the control.
260   * @param  isCritical  Indicates whether the control should be marked
261   *                     critical.
262   * @param  value       The encoded value for the control.  This may be
263   *                     {@code null} if no value was provided.
264   *
265   * @throws  LDAPException  If the provided control cannot be decoded as a get
266   *                         authorization entry response control.
267   */
268  public GetAuthorizationEntryResponseControl(@NotNull final String oid,
269              final boolean isCritical,
270              @Nullable final ASN1OctetString value)
271         throws LDAPException
272  {
273    super(oid, isCritical,  value);
274
275    if (value == null)
276    {
277      throw new LDAPException(ResultCode.DECODING_ERROR,
278           ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_NO_VALUE.get());
279    }
280
281    try
282    {
283      boolean       isAuth   = false;
284      boolean       idsMatch = false;
285      String        nID      = null;
286      String        zID      = null;
287      ReadOnlyEntry nEntry   = null;
288      ReadOnlyEntry zEntry   = null;
289
290      final ASN1Element valElement = ASN1Element.decode(value.getValue());
291      for (final ASN1Element e :
292           ASN1Sequence.decodeAsSequence(valElement).elements())
293      {
294        switch (e.getType())
295        {
296          case TYPE_IS_AUTHENTICATED:
297            isAuth = ASN1Boolean.decodeAsBoolean(e).booleanValue();
298            break;
299          case TYPE_IDENTITIES_MATCH:
300            idsMatch = ASN1Boolean.decodeAsBoolean(e).booleanValue();
301            break;
302          case TYPE_AUTHN_ENTRY:
303            final Object[] nObjects = decodeAuthEntry(e);
304            nID = (String) nObjects[0];
305            nEntry = (ReadOnlyEntry) nObjects[1];
306            break;
307          case TYPE_AUTHZ_ENTRY:
308            final Object[] zObjects = decodeAuthEntry(e);
309            zID = (String) zObjects[0];
310            zEntry = (ReadOnlyEntry) zObjects[1];
311            break;
312          default:
313            throw new LDAPException(ResultCode.DECODING_ERROR,
314                 ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_VALUE_TYPE.get(
315                      StaticUtils.toHex(e.getType())));
316        }
317      }
318
319      isAuthenticated = isAuth;
320      identitiesMatch = idsMatch;
321      authNID         = nID;
322      authNEntry      = nEntry;
323      authZID         = zID;
324      authZEntry      = zEntry;
325    }
326    catch (final Exception e)
327    {
328      Debug.debugException(e);
329      throw new LDAPException(ResultCode.DECODING_ERROR,
330           ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_CANNOT_DECODE_VALUE.get(
331                StaticUtils.getExceptionMessage(e)),
332           e);
333    }
334  }
335
336
337
338  /**
339   * {@inheritDoc}
340   */
341  @Override()
342  @NotNull()
343  public GetAuthorizationEntryResponseControl decodeControl(
344              @NotNull final String oid,
345              final boolean isCritical,
346              @Nullable final ASN1OctetString value)
347         throws LDAPException
348  {
349    return new GetAuthorizationEntryResponseControl(oid, isCritical, value);
350  }
351
352
353
354  /**
355   * Extracts a get authorization entry response control from the provided
356   * result.
357   *
358   * @param  result  The result from which to retrieve the get authorization
359   *                 entry response control.
360   *
361   * @return  The get authorization entry response control contained in the
362   *          provided result, or {@code null} if the result did not contain a
363   *          get authorization entry response control.
364   *
365   * @throws  LDAPException  If a problem is encountered while attempting to
366   *                         decode the get authorization entry response control
367   *                         contained in the provided result.
368   */
369  @Nullable()
370  public static GetAuthorizationEntryResponseControl get(
371                     @NotNull final BindResult result)
372         throws LDAPException
373  {
374    final Control c =
375         result.getResponseControl(GET_AUTHORIZATION_ENTRY_RESPONSE_OID);
376    if (c == null)
377    {
378      return null;
379    }
380
381    if (c instanceof GetAuthorizationEntryResponseControl)
382    {
383      return (GetAuthorizationEntryResponseControl) c;
384    }
385    else
386    {
387      return new GetAuthorizationEntryResponseControl(c.getOID(),
388           c.isCritical(), c.getValue());
389    }
390  }
391
392
393
394  /**
395   * Encodes the provided information appropriately for use as the value of this
396   * control.
397   *
398   * @param  isAuthenticated  Indicates whether the client is authenticated.
399   * @param  identitiesMatch  Indicates whether the authentication identity is
400   *                          the same as the authorization identity.
401   * @param  authNID          The string that may be used to reference the
402   *                          authentication identity.  It may be {@code null}
403   *                          if information about the authentication identity
404   *                          is not to be included, or if the identifier should
405   *                          be derived from the DN.
406   * @param  authNEntry       The entry for the authentication identity.  It may
407   *                          be {@code null} if the information about the
408   *                          authentication identity is not to be included.
409   * @param  authZID          The string that may be used to reference the
410   *                          authorization identity.  It may be {@code null}
411   *                          if information about the authentication identity
412   *                          is not to be included, if the identifier should
413   *                          be derived from the DN, or if the authentication
414   *                          and authorization identities are the same.
415   * @param  authZEntry       The entry for the authentication identity.  It may
416   *                          be {@code null} if the information about the
417   *                          authentication identity is not to be included, or
418   *                          if the authentication and authorization identities
419   *                          are the same.
420   *
421   * @return  The ASN.1 octet string suitable for use as the control value.
422   */
423  @NotNull()
424  private static ASN1OctetString encodeValue(final boolean isAuthenticated,
425                      final boolean identitiesMatch,
426                      @Nullable final String authNID,
427                      @Nullable final ReadOnlyEntry authNEntry,
428                      @Nullable final String authZID,
429                      @Nullable final ReadOnlyEntry authZEntry)
430  {
431    final ArrayList<ASN1Element> elements = new ArrayList<>(4);
432    elements.add(new ASN1Boolean(TYPE_IS_AUTHENTICATED, isAuthenticated));
433    elements.add(new ASN1Boolean(TYPE_IDENTITIES_MATCH, identitiesMatch));
434
435    if (authNEntry != null)
436    {
437      elements.add(encodeAuthEntry(TYPE_AUTHN_ENTRY, authNID, authNEntry));
438    }
439
440    if (authZEntry != null)
441    {
442      elements.add(encodeAuthEntry(TYPE_AUTHZ_ENTRY, authZID, authZEntry));
443    }
444
445    return new ASN1OctetString(new ASN1Sequence(elements).encode());
446  }
447
448
449
450  /**
451   * Encodes the provided information as appropriate for an auth entry.
452   *
453   * @param  type       The BER type to use for the element.
454   * @param  authID     The authID to be encoded, if available.
455   * @param  authEntry  The entry to be encoded.
456   *
457   * @return  The ASN.1 sequence containing the encoded auth entry.
458   */
459  @NotNull()
460  private static ASN1Sequence encodeAuthEntry(final byte type,
461                      @Nullable final String authID,
462                      @NotNull final ReadOnlyEntry authEntry)
463  {
464    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
465
466    if (authID != null)
467    {
468      elements.add(new ASN1OctetString(TYPE_AUTHID, authID));
469    }
470
471    elements.add(new ASN1OctetString(TYPE_AUTHDN, authEntry.getDN()));
472
473    final Collection<Attribute> attributes = authEntry.getAttributes();
474    final ArrayList<ASN1Element> attrElements =
475         new ArrayList<>(attributes.size());
476    for (final Attribute a : attributes)
477    {
478      attrElements.add(a.encode());
479    }
480    elements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements));
481
482    return new ASN1Sequence(type, elements);
483  }
484
485
486
487  /**
488   * Decodes the provided ASN.1 element into an array of auth entry elements.
489   * The first element of the array will be the auth ID, and the second element
490   * will be the read-only entry.
491   *
492   * @param  element  The element to decode.
493   *
494   * @return  The decoded array of elements.
495   *
496   * @throws  ASN1Exception  If a problem occurs while performing ASN.1 parsing.
497   *
498   * @throws  LDAPException  If a problem occurs while performing LDAP parsing.
499   */
500  @NotNull()
501  private static Object[] decodeAuthEntry(@NotNull final ASN1Element element)
502          throws ASN1Exception, LDAPException
503  {
504    String authID = null;
505    String authDN = null;
506    final ArrayList<Attribute> attrs = new ArrayList<>(20);
507
508    for (final ASN1Element e :
509         ASN1Sequence.decodeAsSequence(element).elements())
510    {
511      switch (e.getType())
512      {
513        case TYPE_AUTHID:
514          authID = ASN1OctetString.decodeAsOctetString(e).stringValue();
515          break;
516        case TYPE_AUTHDN:
517          authDN = ASN1OctetString.decodeAsOctetString(e).stringValue();
518          break;
519        case TYPE_ATTRIBUTES:
520          for (final ASN1Element ae :
521               ASN1Sequence.decodeAsSequence(e).elements())
522          {
523            attrs.add(Attribute.decode(ASN1Sequence.decodeAsSequence(ae)));
524          }
525          break;
526        default:
527          throw new LDAPException(ResultCode.DECODING_ERROR,
528               ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_ENTRY_TYPE.get(
529                    StaticUtils.toHex(e.getType())));
530      }
531    }
532
533    return new Object[] { authID, new ReadOnlyEntry(authDN, attrs) };
534  }
535
536
537
538  /**
539   * Indicates whether the client is authenticated.
540   *
541   * @return  {@code true} if the client is authenticated, or {@code false} if
542   *          not.
543   */
544  public boolean isAuthenticated()
545  {
546    return isAuthenticated;
547  }
548
549
550
551  /**
552   * Indicates whether the authentication identity and the authorization
553   * identity reference the same user.
554   *
555   * @return  {@code true} if both the authentication identity and the
556   *          authorization identity reference the same user, or {@code false}
557   *          if not.
558   */
559  public boolean identitiesMatch()
560  {
561    return identitiesMatch;
562  }
563
564
565
566  /**
567   * Retrieves the identifier that may be used to reference the authentication
568   * identity in the directory server, if it is available.
569   *
570   * @return  The identifier that may be used to reference the authentication
571   *          identity in the directory server, or {@code null} if it is not
572   *          available.
573   */
574  @Nullable()
575  public String getAuthNID()
576  {
577    if ((authNID == null) && identitiesMatch)
578    {
579      return authZID;
580    }
581
582    return authNID;
583  }
584
585
586
587  /**
588   * Retrieves the entry for the user specified as the authentication identity,
589   * if it is available.
590   *
591   * @return  The entry for the user specified as the authentication identity,
592   *          or {@code null} if it is not available.
593   */
594  @Nullable()
595  public ReadOnlyEntry getAuthNEntry()
596  {
597    if ((authNEntry == null) && identitiesMatch)
598    {
599      return authZEntry;
600    }
601
602    return authNEntry;
603  }
604
605
606
607  /**
608   * Retrieves the identifier that may be used to reference the authorization
609   * identity in the directory server, if it is available.
610   *
611   * @return  The identifier that may be used to reference the authorization
612   *          identity in the directory server, or {@code null} if it is not
613   *          available.
614   */
615  @Nullable()
616  public String getAuthZID()
617  {
618    if ((authZID == null) && identitiesMatch)
619    {
620      return authNID;
621    }
622
623    return authZID;
624  }
625
626
627
628  /**
629   * Retrieves the entry for the user specified as the authorization identity,
630   * if it is available.
631   *
632   * @return  The entry for the user specified as the authorization identity,
633   *          or {@code null} if it is not available.
634   */
635  @Nullable()
636  public ReadOnlyEntry getAuthZEntry()
637  {
638    if ((authZEntry == null) && identitiesMatch)
639    {
640      return authNEntry;
641    }
642
643    return authZEntry;
644  }
645
646
647
648  /**
649   * {@inheritDoc}
650   */
651  @Override()
652  @NotNull()
653  public String getControlName()
654  {
655    return INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get();
656  }
657
658
659
660  /**
661   * {@inheritDoc}
662   */
663  @Override()
664  public void toString(@NotNull final StringBuilder buffer)
665  {
666    buffer.append("GetAuthorizationEntryResponseControl(identitiesMatch=");
667    buffer.append(identitiesMatch);
668
669    if (authNID != null)
670    {
671      buffer.append(", authNID='");
672      buffer.append(authNID);
673      buffer.append('\'');
674    }
675
676    if (authNEntry != null)
677    {
678      buffer.append(", authNEntry=");
679      authNEntry.toString(buffer);
680    }
681
682    if (authZID != null)
683    {
684      buffer.append(", authZID='");
685      buffer.append(authZID);
686      buffer.append('\'');
687    }
688
689    if (authZEntry != null)
690    {
691      buffer.append(", authZEntry=");
692      authZEntry.toString(buffer);
693    }
694
695    buffer.append(')');
696  }
697}