001/*
002 * Copyright 2008-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 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.NotMutable;
041import com.unboundid.util.ThreadSafety;
042import com.unboundid.util.ThreadSafetyLevel;
043
044import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
045import static com.unboundid.util.Debug.*;
046import static com.unboundid.util.StaticUtils.*;
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 Alcatel-Lucent 8661
059 *   server products.  These classes provide support for proprietary
060 *   functionality or for external specifications that are not considered stable
061 *   or mature enough to be guaranteed to work in an interoperable way with
062 *   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                      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      debugException(e);
311      throw new LDAPException(ResultCode.DECODING_ERROR,
312           ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_CANNOT_DECODE_VALUE.get(
313                getExceptionMessage(e)), e);
314    }
315  }
316
317
318
319  /**
320   * {@inheritDoc}
321   */
322  @Override()
323  public GetAuthorizationEntryResponseControl decodeControl(final String oid,
324                                                   final boolean isCritical,
325                                                   final ASN1OctetString value)
326         throws LDAPException
327  {
328    return new GetAuthorizationEntryResponseControl(oid, isCritical, value);
329  }
330
331
332
333  /**
334   * Extracts a get authorization entry response control from the provided
335   * result.
336   *
337   * @param  result  The result from which to retrieve the get authorization
338   *                 entry response control.
339   *
340   * @return  The get authorization entry response control contained in the
341   *          provided result, or {@code null} if the result did not contain a
342   *          get authorization entry response control.
343   *
344   * @throws  LDAPException  If a problem is encountered while attempting to
345   *                         decode the get authorization entry response control
346   *                         contained in the provided result.
347   */
348  public static GetAuthorizationEntryResponseControl
349                     get(final BindResult result)
350         throws LDAPException
351  {
352    final Control c =
353         result.getResponseControl(GET_AUTHORIZATION_ENTRY_RESPONSE_OID);
354    if (c == null)
355    {
356      return null;
357    }
358
359    if (c instanceof GetAuthorizationEntryResponseControl)
360    {
361      return (GetAuthorizationEntryResponseControl) c;
362    }
363    else
364    {
365      return new GetAuthorizationEntryResponseControl(c.getOID(),
366           c.isCritical(), c.getValue());
367    }
368  }
369
370
371
372  /**
373   * Encodes the provided information appropriately for use as the value of this
374   * control.
375   *
376   * @param  isAuthenticated  Indicates whether the client is authenticated.
377   * @param  identitiesMatch  Indicates whether the authentication identity is
378   *                          the same as the authorization identity.
379   * @param  authNID          The string that may be used to reference the
380   *                          authentication identity.  It may be {@code null}
381   *                          if information about the authentication identity
382   *                          is not to be included, or if the identifier should
383   *                          be derived from the DN.
384   * @param  authNEntry       The entry for the authentication identity.  It may
385   *                          be {@code null} if the information about the
386   *                          authentication identity is not to be included.
387   * @param  authZID          The string that may be used to reference the
388   *                          authorization identity.  It may be {@code null}
389   *                          if information about the authentication identity
390   *                          is not to be included, if the identifier should
391   *                          be derived from the DN, or if the authentication
392   *                          and authorization identities are the same.
393   * @param  authZEntry       The entry for the authentication identity.  It may
394   *                          be {@code null} if the information about the
395   *                          authentication identity is not to be included, or
396   *                          if the authentication and authorization identities
397   *                          are the same.
398   *
399   * @return  The ASN.1 octet string suitable for use as the control value.
400   */
401  private static ASN1OctetString encodeValue(final boolean isAuthenticated,
402                                             final boolean identitiesMatch,
403                                             final String authNID,
404                                             final ReadOnlyEntry authNEntry,
405                                             final String authZID,
406                                             final ReadOnlyEntry authZEntry)
407  {
408    final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4);
409    elements.add(new ASN1Boolean(TYPE_IS_AUTHENTICATED, isAuthenticated));
410    elements.add(new ASN1Boolean(TYPE_IDENTITIES_MATCH, identitiesMatch));
411
412    if (authNEntry != null)
413    {
414      elements.add(encodeAuthEntry(TYPE_AUTHN_ENTRY, authNID, authNEntry));
415    }
416
417    if (authZEntry != null)
418    {
419      elements.add(encodeAuthEntry(TYPE_AUTHZ_ENTRY, authZID, authZEntry));
420    }
421
422    return new ASN1OctetString(new ASN1Sequence(elements).encode());
423  }
424
425
426
427  /**
428   * Encodes the provided information as appropriate for an auth entry.
429   *
430   * @param  type       The BER type to use for the element.
431   * @param  authID     The authID to be encoded, if available.
432   * @param  authEntry  The entry to be encoded.
433   *
434   * @return  The ASN.1 sequence containing the encoded auth entry.
435   */
436  private static ASN1Sequence encodeAuthEntry(final byte type,
437                                              final String authID,
438                                              final ReadOnlyEntry authEntry)
439  {
440    final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
441
442    if (authID != null)
443    {
444      elements.add(new ASN1OctetString(TYPE_AUTHID, authID));
445    }
446
447    elements.add(new ASN1OctetString(TYPE_AUTHDN, authEntry.getDN()));
448
449    final Collection<Attribute> attributes = authEntry.getAttributes();
450    final ArrayList<ASN1Element> attrElements =
451         new ArrayList<ASN1Element>(attributes.size());
452    for (final Attribute a : attributes)
453    {
454      attrElements.add(a.encode());
455    }
456    elements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements));
457
458    return new ASN1Sequence(type, elements);
459  }
460
461
462
463  /**
464   * Decodes the provided ASN.1 element into an array of auth entry elements.
465   * The first element of the array will be the auth ID, and the second element
466   * will be the read-only entry.
467   *
468   * @param  element  The element to decode.
469   *
470   * @return  The decoded array of elements.
471   *
472   * @throws  ASN1Exception  If a problem occurs while performing ASN.1 parsing.
473   *
474   * @throws  LDAPException  If a problem occurs while performing LDAP parsing.
475   */
476  private static Object[] decodeAuthEntry(final ASN1Element element)
477          throws ASN1Exception, LDAPException
478  {
479    String authID = null;
480    String authDN = null;
481    final ArrayList<Attribute> attrs = new ArrayList<Attribute>();
482
483    for (final ASN1Element e :
484         ASN1Sequence.decodeAsSequence(element).elements())
485    {
486      switch (e.getType())
487      {
488        case TYPE_AUTHID:
489          authID = ASN1OctetString.decodeAsOctetString(e).stringValue();
490          break;
491        case TYPE_AUTHDN:
492          authDN = ASN1OctetString.decodeAsOctetString(e).stringValue();
493          break;
494        case TYPE_ATTRIBUTES:
495          for (final ASN1Element ae :
496               ASN1Sequence.decodeAsSequence(e).elements())
497          {
498            attrs.add(Attribute.decode(ASN1Sequence.decodeAsSequence(ae)));
499          }
500          break;
501        default:
502          throw new LDAPException(ResultCode.DECODING_ERROR,
503               ERR_GET_AUTHORIZATION_ENTRY_RESPONSE_INVALID_ENTRY_TYPE.get(
504                    toHex(e.getType())));
505      }
506    }
507
508    return new Object[] { authID, new ReadOnlyEntry(authDN, attrs) };
509  }
510
511
512
513  /**
514   * Indicates whether the client is authenticated.
515   *
516   * @return  {@code true} if the client is authenticated, or {@code false} if
517   *          not.
518   */
519  public boolean isAuthenticated()
520  {
521    return isAuthenticated;
522  }
523
524
525
526  /**
527   * Indicates whether the authentication identity and the authorization
528   * identity reference the same user.
529   *
530   * @return  {@code true} if both the authentication identity and the
531   *          authorization identity reference the same user, or {@code false}
532   *          if not.
533   */
534  public boolean identitiesMatch()
535  {
536    return identitiesMatch;
537  }
538
539
540
541  /**
542   * Retrieves the identifier that may be used to reference the authentication
543   * identity in the directory server, if it is available.
544   *
545   * @return  The identifier that may be used to reference the authentication
546   *          identity in the directory server, or {@code null} if it is not
547   *          available.
548   */
549  public String getAuthNID()
550  {
551    if ((authNID == null) && identitiesMatch)
552    {
553      return authZID;
554    }
555
556    return authNID;
557  }
558
559
560
561  /**
562   * Retrieves the entry for the user specified as the authentication identity,
563   * if it is available.
564   *
565   * @return  The entry for the user specified as the authentication identity,
566   *          or {@code null} if it is not available.
567   */
568  public ReadOnlyEntry getAuthNEntry()
569  {
570    if ((authNEntry == null) && identitiesMatch)
571    {
572      return authZEntry;
573    }
574
575    return authNEntry;
576  }
577
578
579
580  /**
581   * Retrieves the identifier that may be used to reference the authorization
582   * identity in the directory server, if it is available.
583   *
584   * @return  The identifier that may be used to reference the authorization
585   *          identity in the directory server, or {@code null} if it is not
586   *          available.
587   */
588  public String getAuthZID()
589  {
590    if ((authZID == null) && identitiesMatch)
591    {
592      return authNID;
593    }
594
595    return authZID;
596  }
597
598
599
600  /**
601   * Retrieves the entry for the user specified as the authorization identity,
602   * if it is available.
603   *
604   * @return  The entry for the user specified as the authorization identity,
605   *          or {@code null} if it is not available.
606   */
607  public ReadOnlyEntry getAuthZEntry()
608  {
609    if ((authZEntry == null) && identitiesMatch)
610    {
611      return authNEntry;
612    }
613
614    return authZEntry;
615  }
616
617
618
619  /**
620   * {@inheritDoc}
621   */
622  @Override()
623  public String getControlName()
624  {
625    return INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_RESPONSE.get();
626  }
627
628
629
630  /**
631   * {@inheritDoc}
632   */
633  @Override()
634  public void toString(final StringBuilder buffer)
635  {
636    buffer.append("GetAuthorizationEntryResponseControl(identitiesMatch=");
637    buffer.append(identitiesMatch);
638
639    if (authNID != null)
640    {
641      buffer.append(", authNID='");
642      buffer.append(authNID);
643      buffer.append('\'');
644    }
645
646    if (authNEntry != null)
647    {
648      buffer.append(", authNEntry=");
649      authNEntry.toString(buffer);
650    }
651
652    if (authZID != null)
653    {
654      buffer.append(", authZID='");
655      buffer.append(authZID);
656      buffer.append('\'');
657    }
658
659    if (authZEntry != null)
660    {
661      buffer.append(", authZEntry=");
662      authZEntry.toString(buffer);
663    }
664
665    buffer.append(')');
666  }
667}