001    /*
002     * Copyright 2007-2013 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2013 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.controls;
022    
023    
024    
025    import com.unboundid.asn1.ASN1OctetString;
026    import com.unboundid.ldap.sdk.Control;
027    import com.unboundid.ldap.sdk.DecodeableControl;
028    import com.unboundid.ldap.sdk.LDAPException;
029    import com.unboundid.ldap.sdk.LDAPResult;
030    import com.unboundid.ldap.sdk.ResultCode;
031    import com.unboundid.util.NotMutable;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    
035    import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
036    import static com.unboundid.util.Debug.*;
037    
038    
039    
040    /**
041     * This class provides an implementation of the password expired control as
042     * described in draft-vchu-ldap-pwd-policy.  It may be included in the response
043     * for an unsuccessful bind operation to indicate that the reason for the
044     * failure is that the target user's password has expired and must be reset
045     * before the user will be allowed to authenticate.  Some servers may also
046     * include this control in a successful bind response to indicate that the
047     * authenticated user must change his or her password before being allowed to
048     * perform any other operation.
049     * <BR><BR>
050     * No request control is required to trigger the server to send the password
051     * expired response control.  If the server supports the use of this control and
052     * the corresponding bind operation meets the criteria for this control to be
053     * included in the response, then it will be returned to the client.
054     * <BR><BR>
055     * <H2>Example</H2>
056     * The following example demonstrates a process that may be used to perform a
057     * simple bind to authenticate against the server and handle any password
058     * expired or password expiring control that may be included in the response:
059     * <PRE>
060     *   BindRequest bindRequest =
061     *        new SimpleBindRequest("uid=john.doe,ou=People,dc=example,dc=com",
062     *                              "password");
063     *   try
064     *   {
065     *     BindResult bindResult = connection.bind(bindRequest);
066     *     for (Control c : bindResult.getResponseControls())
067     *     {
068     *       if (c instanceof PasswordExpiringControl)
069     *       {
070     *         System.err.println("WARNING:  Your password will expire in " +
071     *              ((PasswordExpiringControl) c).getSecondsUntilExpiration() +
072     *              " seconds.");
073     *       }
074     *       else if (c instanceof PasswordExpiredControl)
075     *       {
076     *         System.err.println("WARNING:  You must change your password " +
077     *              "before you will be allowed to perform any other operations.");
078     *       }
079     *     }
080     *   }
081     *   catch (LDAPException le)
082     *   {
083     *     for (Control c : le.getResponseControls())
084     *     {
085     *       if (c instanceof PasswordExpiredControl)
086     *       {
087     *         System.err.println("ERROR:  Your password is expired.");
088     *       }
089     *     }
090     *   }
091     * </PRE>
092     */
093    @NotMutable()
094    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
095    public final class PasswordExpiredControl
096           extends Control
097           implements DecodeableControl
098    {
099      /**
100       * The OID (2.16.840.1.113730.3.4.4) for the password expired response
101       * control.
102       */
103      public static final String PASSWORD_EXPIRED_OID = "2.16.840.1.113730.3.4.4";
104    
105    
106    
107      /**
108       * The serial version UID for this serializable class.
109       */
110      private static final long serialVersionUID = -2731704592689892224L;
111    
112    
113    
114      /**
115       * Creates a new password expired control.
116       */
117      public PasswordExpiredControl()
118      {
119        super(PASSWORD_EXPIRED_OID, false, new ASN1OctetString("0"));
120      }
121    
122    
123    
124      /**
125       * Creates a new password expired control with the provided information.
126       *
127       * @param  oid         The OID for the control.
128       * @param  isCritical  Indicates whether the control should be marked
129       *                     critical.
130       * @param  value       The encoded value for the control.  This may be
131       *                     {@code null} if no value was provided.
132       *
133       * @throws  LDAPException  If the provided control cannot be decoded as a
134       *                         password expired response control.
135       */
136      public PasswordExpiredControl(final String oid, final boolean isCritical,
137                                    final ASN1OctetString value)
138             throws LDAPException
139      {
140        super(oid, isCritical, value);
141    
142        if (value == null)
143        {
144          throw new LDAPException(ResultCode.DECODING_ERROR,
145                                  ERR_PW_EXPIRED_NO_VALUE.get());
146        }
147    
148        try
149        {
150          Integer.parseInt(value.stringValue());
151        }
152        catch (NumberFormatException nfe)
153        {
154          debugException(nfe);
155          throw new LDAPException(ResultCode.DECODING_ERROR,
156                                  ERR_PW_EXPIRED_VALUE_NOT_INTEGER.get(), nfe);
157        }
158      }
159    
160    
161    
162      /**
163       * {@inheritDoc}
164       */
165      public PasswordExpiredControl
166                  decodeControl(final String oid, final boolean isCritical,
167                                final ASN1OctetString value)
168             throws LDAPException
169      {
170        return new PasswordExpiredControl(oid, isCritical, value);
171      }
172    
173    
174    
175      /**
176       * Extracts a password expired control from the provided result.
177       *
178       * @param  result  The result from which to retrieve the password expired
179       *                 control.
180       *
181       * @return  The password expired control contained in the provided result, or
182       *          {@code null} if the result did not contain a password expired
183       *          control.
184       *
185       * @throws  LDAPException  If a problem is encountered while attempting to
186       *                         decode the password expired control contained in
187       *                         the provided result.
188       */
189      public static PasswordExpiredControl get(final LDAPResult result)
190             throws LDAPException
191      {
192        final Control c = result.getResponseControl(PASSWORD_EXPIRED_OID);
193        if (c == null)
194        {
195          return null;
196        }
197    
198        if (c instanceof PasswordExpiredControl)
199        {
200          return (PasswordExpiredControl) c;
201        }
202        else
203        {
204          return new PasswordExpiredControl(c.getOID(), c.isCritical(),
205               c.getValue());
206        }
207      }
208    
209    
210    
211      /**
212       * {@inheritDoc}
213       */
214      @Override()
215      public String getControlName()
216      {
217        return INFO_CONTROL_NAME_PW_EXPIRED.get();
218      }
219    
220    
221    
222      /**
223       * {@inheritDoc}
224       */
225      @Override()
226      public void toString(final StringBuilder buffer)
227      {
228        buffer.append("PasswordExpiredControl(isCritical=");
229        buffer.append(isCritical());
230        buffer.append(')');
231      }
232    }