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