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