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