001/*
002 * Copyright 2007-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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 */
021package com.unboundid.ldap.sdk.controls;
022
023
024
025import com.unboundid.asn1.ASN1OctetString;
026import com.unboundid.ldap.sdk.Control;
027import com.unboundid.ldap.sdk.DecodeableControl;
028import com.unboundid.ldap.sdk.LDAPException;
029import com.unboundid.ldap.sdk.LDAPResult;
030import com.unboundid.ldap.sdk.ResultCode;
031import com.unboundid.util.NotMutable;
032import com.unboundid.util.ThreadSafety;
033import com.unboundid.util.ThreadSafetyLevel;
034
035import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
036import 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 * // Send a simple bind request to the directory server.
061 * BindRequest bindRequest =
062 *      new SimpleBindRequest("uid=test.user,ou=People,dc=example,dc=com",
063 *           "password");
064 * BindResult bindResult;
065 * boolean bindSuccessful;
066 * boolean passwordExpired;
067 * boolean passwordAboutToExpire;
068 * try
069 * {
070 *   bindResult = connection.bind(bindRequest);
071 *
072 *   // If we got here, the bind was successful and we know the password was
073 *   // not expired.  However, we shouldn't ignore the result because the
074 *   // password might be about to expire.  To determine whether that is the
075 *   // case, we should see if the bind result included a password expiring
076 *   // control.
077 *   bindSuccessful = true;
078 *   passwordExpired = false;
079 *
080 *   PasswordExpiringControl expiringControl =
081 *        PasswordExpiringControl.get(bindResult);
082 *   if (expiringControl != null)
083 *   {
084 *     passwordAboutToExpire = true;
085 *     int secondsToExpiration = expiringControl.getSecondsUntilExpiration();
086 *   }
087 *   else
088 *   {
089 *     passwordAboutToExpire = false;
090 *   }
091 * }
092 * catch (LDAPException le)
093 * {
094 *   // If we got here, then the bind failed.  The failure may or may not have
095 *   // been due to an expired password.  To determine that, we should see if
096 *   // the bind result included a password expired control.
097 *   bindSuccessful = false;
098 *   passwordAboutToExpire = false;
099 *   bindResult = new BindResult(le.toLDAPResult());
100 *   ResultCode resultCode = le.getResultCode();
101 *   String errorMessageFromServer = le.getDiagnosticMessage();
102 *
103 *   PasswordExpiredControl expiredControl =
104 *        PasswordExpiredControl.get(le);
105 *   if (expiredControl != null)
106 *   {
107 *     passwordExpired = true;
108 *   }
109 *   else
110 *   {
111 *     passwordExpired = false;
112 *   }
113 * }
114 * </PRE>
115 */
116@NotMutable()
117@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
118public final class PasswordExpiredControl
119       extends Control
120       implements DecodeableControl
121{
122  /**
123   * The OID (2.16.840.1.113730.3.4.4) for the password expired response
124   * control.
125   */
126  public static final String PASSWORD_EXPIRED_OID = "2.16.840.1.113730.3.4.4";
127
128
129
130  /**
131   * The serial version UID for this serializable class.
132   */
133  private static final long serialVersionUID = -2731704592689892224L;
134
135
136
137  /**
138   * Creates a new password expired control.
139   */
140  public PasswordExpiredControl()
141  {
142    super(PASSWORD_EXPIRED_OID, false, new ASN1OctetString("0"));
143  }
144
145
146
147  /**
148   * Creates a new password expired control with the provided information.
149   *
150   * @param  oid         The OID for the control.
151   * @param  isCritical  Indicates whether the control should be marked
152   *                     critical.
153   * @param  value       The encoded value for the control.  This may be
154   *                     {@code null} if no value was provided.
155   *
156   * @throws  LDAPException  If the provided control cannot be decoded as a
157   *                         password expired response control.
158   */
159  public PasswordExpiredControl(final String oid, final boolean isCritical,
160                                final ASN1OctetString value)
161         throws LDAPException
162  {
163    super(oid, isCritical, value);
164
165    if (value == null)
166    {
167      throw new LDAPException(ResultCode.DECODING_ERROR,
168                              ERR_PW_EXPIRED_NO_VALUE.get());
169    }
170
171    try
172    {
173      Integer.parseInt(value.stringValue());
174    }
175    catch (NumberFormatException nfe)
176    {
177      debugException(nfe);
178      throw new LDAPException(ResultCode.DECODING_ERROR,
179                              ERR_PW_EXPIRED_VALUE_NOT_INTEGER.get(), nfe);
180    }
181  }
182
183
184
185  /**
186   * {@inheritDoc}
187   */
188  public PasswordExpiredControl
189              decodeControl(final String oid, final boolean isCritical,
190                            final ASN1OctetString value)
191         throws LDAPException
192  {
193    return new PasswordExpiredControl(oid, isCritical, value);
194  }
195
196
197
198  /**
199   * Extracts a password expired control from the provided result.
200   *
201   * @param  result  The result from which to retrieve the password expired
202   *                 control.
203   *
204   * @return  The password expired control contained in the provided result, or
205   *          {@code null} if the result did not contain a password expired
206   *          control.
207   *
208   * @throws  LDAPException  If a problem is encountered while attempting to
209   *                         decode the password expired control contained in
210   *                         the provided result.
211   */
212  public static PasswordExpiredControl get(final LDAPResult result)
213         throws LDAPException
214  {
215    final Control c = result.getResponseControl(PASSWORD_EXPIRED_OID);
216    if (c == null)
217    {
218      return null;
219    }
220
221    if (c instanceof PasswordExpiredControl)
222    {
223      return (PasswordExpiredControl) c;
224    }
225    else
226    {
227      return new PasswordExpiredControl(c.getOID(), c.isCritical(),
228           c.getValue());
229    }
230  }
231
232
233
234  /**
235   * Extracts a password expired control from the provided exception.
236   *
237   * @param  exception  The exception from which to retrieve the password
238   *                    expired control.
239   *
240   * @return  The password expired control contained in the provided exception,
241   *          or {@code null} if the exception did not contain a password
242   *          expired control.
243   *
244   * @throws  LDAPException  If a problem is encountered while attempting to
245   *                         decode the password expired control contained in
246   *                         the provided exception.
247   */
248  public static PasswordExpiredControl get(final LDAPException exception)
249         throws LDAPException
250  {
251    return get(exception.toLDAPResult());
252  }
253
254
255
256  /**
257   * {@inheritDoc}
258   */
259  @Override()
260  public String getControlName()
261  {
262    return INFO_CONTROL_NAME_PW_EXPIRED.get();
263  }
264
265
266
267  /**
268   * {@inheritDoc}
269   */
270  @Override()
271  public void toString(final StringBuilder buffer)
272  {
273    buffer.append("PasswordExpiredControl(isCritical=");
274    buffer.append(isCritical());
275    buffer.append(')');
276  }
277}