001    /*
002     * Copyright 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.extensions;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.Iterator;
029    import java.util.List;
030    
031    import com.unboundid.asn1.ASN1Boolean;
032    import com.unboundid.asn1.ASN1Element;
033    import com.unboundid.asn1.ASN1OctetString;
034    import com.unboundid.asn1.ASN1Sequence;
035    import com.unboundid.ldap.sdk.Control;
036    import com.unboundid.ldap.sdk.ExtendedResult;
037    import com.unboundid.ldap.sdk.LDAPException;
038    import com.unboundid.ldap.sdk.ResultCode;
039    import com.unboundid.util.Debug;
040    import com.unboundid.util.NotMutable;
041    import com.unboundid.util.StaticUtils;
042    import com.unboundid.util.ThreadSafety;
043    import com.unboundid.util.ThreadSafetyLevel;
044    
045    import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
046    
047    
048    
049    /**
050     * <BLOCKQUOTE>
051     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
052     *   LDAP SDK for Java.  It is not available for use in applications that
053     *   include only the Standard Edition of the LDAP SDK, and is not supported for
054     *   use in conjunction with non-UnboundID products.
055     * </BLOCKQUOTE>
056     * This class provides an implementation of an extended result that may be used
057     * to provide information about which one-time password delivery mechanisms are
058     * supported for a user.
059     * <BR><BR>
060     * If the request was processed successfully, then the extended result will have
061     * an OID of 1.3.6.1.4.1.30221.2.6.48 and a value with the following encoding:
062     * <BR><BR>
063     * <PRE>
064     *   GetSupportedOTPDeliveryMechanismsResult ::= SEQUENCE OF SEQUENCE {
065     *        deliveryMechanism     [0] OCTET STRING,
066     *        isSupported           [1] BOOLEAN OPTIONAL,
067     *        recipientID           [2] OCTET STRING OPTIONAL,
068     *        ... }
069     * </PRE>
070     *
071     * @see  GetSupportedOTPDeliveryMechanismsExtendedRequest
072     */
073    @NotMutable()
074    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
075    public final class GetSupportedOTPDeliveryMechanismsExtendedResult
076           extends ExtendedResult
077    {
078      /**
079       * The OID (1.3.6.1.4.1.30221.2.6.48) for the get supported one-time password
080       * delivery mechanisms extended result.
081       */
082      public static final String GET_SUPPORTED_OTP_DELIVERY_MECHANISMS_RESULT_OID =
083           "1.3.6.1.4.1.30221.2.6.48";
084    
085    
086    
087      /**
088       * The BER type for the delivery mechanism element.
089       */
090      private static final byte TYPE_DELIVERY_MECHANISM = (byte) 0x80;
091    
092    
093    
094      /**
095       * The BER type for the is supported element.
096       */
097      private static final byte TYPE_IS_SUPPORTED = (byte) 0x81;
098    
099    
100    
101      /**
102       * The BER type for the recipient ID element.
103       */
104      private static final byte TYPE_RECIPIENT_ID = (byte) 0x82;
105    
106    
107    
108      /**
109       * The serial version UID for this serializable class.
110       */
111      private static final long serialVersionUID = -1811121368502797059L;
112    
113    
114    
115      // The list of supported delivery mechanism information for this result.
116      private final List<SupportedOTPDeliveryMechanismInfo> deliveryMechanismInfo;
117    
118    
119    
120      /**
121       * Decodes the provided extended result as a get supported OTP delivery
122       * mechanisms result.
123       *
124       * @param  result  The extended result to decode as a get supported OTP
125       *                 delivery mechanisms result.
126       *
127       * @throws  LDAPException  If the provided extended result cannot be decoded
128       *                         as a get supported OTP delivery mechanisms result.
129       */
130      public GetSupportedOTPDeliveryMechanismsExtendedResult(
131                  final ExtendedResult result)
132             throws LDAPException
133      {
134        super(result);
135    
136        final ASN1OctetString value = result.getValue();
137        if (value == null)
138        {
139          deliveryMechanismInfo = Collections.emptyList();
140        }
141        else
142        {
143          try
144          {
145            final ASN1Element[] elements =
146                 ASN1Sequence.decodeAsSequence(value.getValue()).elements();
147            final ArrayList<SupportedOTPDeliveryMechanismInfo> mechInfo =
148                 new ArrayList<SupportedOTPDeliveryMechanismInfo>(elements.length);
149            for (final ASN1Element e : elements)
150            {
151              final ASN1Element[] infoElements =
152                   ASN1Sequence.decodeAsSequence(e).elements();
153              final String name = ASN1OctetString.decodeAsOctetString(
154                   infoElements[0]).stringValue();
155    
156              Boolean isSupported = null;
157              String recipientID = null;
158              for (int i=1; i < infoElements.length; i++)
159              {
160                switch (infoElements[i].getType())
161                {
162                  case TYPE_IS_SUPPORTED:
163                    isSupported = ASN1Boolean.decodeAsBoolean(
164                         infoElements[i]).booleanValue();
165                    break;
166    
167                  case TYPE_RECIPIENT_ID:
168                    recipientID = ASN1OctetString.decodeAsOctetString(
169                         infoElements[i]).stringValue();
170                    break;
171    
172                  default:
173                    throw new LDAPException(ResultCode.DECODING_ERROR,
174                         ERR_GET_SUPPORTED_OTP_MECH_RESULT_UNKNOWN_ELEMENT.get(
175                              StaticUtils.toHex(infoElements[i].getType())));
176                }
177              }
178    
179              mechInfo.add(new SupportedOTPDeliveryMechanismInfo(name, isSupported,
180                   recipientID));
181            }
182    
183            deliveryMechanismInfo = Collections.unmodifiableList(mechInfo);
184          }
185          catch (final LDAPException le)
186          {
187            Debug.debugException(le);
188            throw le;
189          }
190          catch (final Exception e)
191          {
192            Debug.debugException(e);
193            throw new LDAPException(ResultCode.DECODING_ERROR,
194                 ERR_GET_SUPPORTED_OTP_MECH_RESULT_CANNOT_DECODE.get(
195                      StaticUtils.getExceptionMessage(e)),
196                 e);
197          }
198        }
199      }
200    
201    
202    
203      /**
204       * Creates a new get supported OTP delivery mechanisms extended result object
205       * with the provided information.
206       *
207       * @param  messageID              The message ID for the LDAP message that is
208       *                                associated with this LDAP result.
209       * @param  resultCode             The result code from the response.  It must
210       *                                not be {@code null}.
211       * @param  diagnosticMessage      The diagnostic message from the response, if
212       *                                available.
213       * @param  matchedDN              The matched DN from the response, if
214       *                                available.
215       * @param  referralURLs           The set of referral URLs from the response,
216       *                                if available.
217       * @param  deliveryMechanismInfo  The set of supported delivery mechanism info
218       *                                for the result, if appropriate.  It should
219       *                                be {@code null} or empty for non-success
220       *                                results.
221       * @param  controls               The set of controls for the response.  It
222       *                                may be {@code null} or empty if no controls
223       *                                are needed.
224       */
225      public GetSupportedOTPDeliveryMechanismsExtendedResult(final int messageID,
226                  final ResultCode resultCode, final String diagnosticMessage,
227                  final String matchedDN, final String[] referralURLs,
228                  final Collection<SupportedOTPDeliveryMechanismInfo>
229                       deliveryMechanismInfo,
230                  final Control... controls)
231      {
232        super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
233             (resultCode == ResultCode.SUCCESS ?
234                  GET_SUPPORTED_OTP_DELIVERY_MECHANISMS_RESULT_OID : null),
235             encodeValue(resultCode, deliveryMechanismInfo), controls);
236    
237        if ((deliveryMechanismInfo == null) || deliveryMechanismInfo.isEmpty())
238        {
239          this.deliveryMechanismInfo = Collections.emptyList();
240        }
241        else
242        {
243          this.deliveryMechanismInfo = Collections.unmodifiableList(
244               new ArrayList<SupportedOTPDeliveryMechanismInfo>(
245                    deliveryMechanismInfo));
246        }
247      }
248    
249    
250    
251      /**
252       * Encodes the provided information into an appropriate format for the value
253       * of this extended operation.
254       *
255       * @param  resultCode             The result code from the response.  It must
256       *                                not be {@code null}.
257       * @param  deliveryMechanismInfo  The set of supported delivery mechanism info
258       *                                for the result, if appropriate.  It should
259       *                                be {@code null} or empty for non-success
260       *                                results.
261       *
262       * @return  The ASN.1 octet string containing the encoded value.
263       */
264      private static ASN1OctetString encodeValue(final ResultCode resultCode,
265                          final Collection<SupportedOTPDeliveryMechanismInfo>
266                               deliveryMechanismInfo)
267    
268      {
269        if (resultCode != ResultCode.SUCCESS)
270        {
271          return null;
272        }
273    
274        if ((deliveryMechanismInfo == null) || deliveryMechanismInfo.isEmpty())
275        {
276          return new ASN1OctetString(new ASN1Sequence().encode());
277        }
278    
279        final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(
280             deliveryMechanismInfo.size());
281        for (final SupportedOTPDeliveryMechanismInfo i : deliveryMechanismInfo)
282        {
283          final ArrayList<ASN1Element> infoElements = new ArrayList<ASN1Element>(3);
284          infoElements.add(new ASN1OctetString(TYPE_DELIVERY_MECHANISM,
285               i.getDeliveryMechanism()));
286    
287          if (i.isSupported() != null)
288          {
289            infoElements.add(new ASN1Boolean(TYPE_IS_SUPPORTED, i.isSupported()));
290          }
291    
292          if (i.getRecipientID() != null)
293          {
294            infoElements.add(new ASN1OctetString(TYPE_RECIPIENT_ID,
295                 i.getRecipientID()));
296          }
297    
298          elements.add(new ASN1Sequence(infoElements));
299        }
300    
301        return new ASN1OctetString(new ASN1Sequence(elements).encode());
302      }
303    
304    
305    
306      /**
307       * Retrieves a list containing information about the OTP delivery mechanisms
308       * supported by the server and which are available for use by the target user,
309       * if available.  Note that it is possible for the same OTP delivery mechanism
310       * to appear in the list multiple times if that mechanism is supported for the
311       * user with multiple recipient IDs (e.g., if the server provides an "Email"
312       * delivery mechanism and a user has multiple email addresses, then the list
313       * may include a separate "Email" delivery mechanism info object for each
314       * of the user's email addresses).
315       *
316       * @return  A list containing information about the OTP delivery mechanisms
317       *          supported by the server and which are available for the target
318       *          user, or an empty list if the server doesn't support  any OTP
319       *          delivery mechanisms or if the request was not processed
320       *          successfully.
321       */
322      public List<SupportedOTPDeliveryMechanismInfo> getDeliveryMechanismInfo()
323      {
324        return deliveryMechanismInfo;
325      }
326    
327    
328    
329      /**
330       * {@inheritDoc}
331       */
332      @Override()
333      public String getExtendedResultName()
334      {
335        return INFO_GET_SUPPORTED_OTP_MECH_RES_NAME.get();
336      }
337    
338    
339    
340      /**
341       * Appends a string representation of this extended result to the provided
342       * buffer.
343       *
344       * @param  buffer  The buffer to which a string representation of this
345       *                 extended result will be appended.
346       */
347      @Override()
348      public void toString(final StringBuilder buffer)
349      {
350        buffer.append("GetSupportedOTPDeliveryMechanismsExtendedResult(" +
351             "resultCode=");
352        buffer.append(getResultCode());
353    
354        final int messageID = getMessageID();
355        if (messageID >= 0)
356        {
357          buffer.append(", messageID=");
358          buffer.append(messageID);
359        }
360    
361        buffer.append("mechanismInfo={");
362        final Iterator<SupportedOTPDeliveryMechanismInfo> mechIterator =
363             deliveryMechanismInfo.iterator();
364        while (mechIterator.hasNext())
365        {
366          mechIterator.next().toString(buffer);
367          if (mechIterator.hasNext())
368          {
369            buffer.append(", ");
370          }
371        }
372        buffer.append('}');
373    
374        final String diagnosticMessage = getDiagnosticMessage();
375        if (diagnosticMessage != null)
376        {
377          buffer.append(", diagnosticMessage='");
378          buffer.append(diagnosticMessage);
379          buffer.append('\'');
380        }
381    
382        final String matchedDN = getMatchedDN();
383        if (matchedDN != null)
384        {
385          buffer.append(", matchedDN='");
386          buffer.append(matchedDN);
387          buffer.append('\'');
388        }
389    
390        final String[] referralURLs = getReferralURLs();
391        if (referralURLs.length > 0)
392        {
393          buffer.append(", referralURLs={");
394          for (int i=0; i < referralURLs.length; i++)
395          {
396            if (i > 0)
397            {
398              buffer.append(", ");
399            }
400    
401            buffer.append('\'');
402            buffer.append(referralURLs[i]);
403            buffer.append('\'');
404          }
405          buffer.append('}');
406        }
407    
408        final Control[] responseControls = getResponseControls();
409        if (responseControls.length > 0)
410        {
411          buffer.append(", responseControls={");
412          for (int i=0; i < responseControls.length; i++)
413          {
414            if (i > 0)
415            {
416              buffer.append(", ");
417            }
418    
419            buffer.append(responseControls[i]);
420          }
421          buffer.append('}');
422        }
423    
424        buffer.append(')');
425      }
426    }