001/*
002 * Copyright 2007-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2018 Ping Identity Corporation
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.Debug;
032import com.unboundid.util.NotMutable;
033import com.unboundid.util.ThreadSafety;
034import com.unboundid.util.ThreadSafetyLevel;
035
036import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
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 (final NumberFormatException nfe)
176    {
177      Debug.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  @Override()
189  public PasswordExpiredControl
190              decodeControl(final String oid, final boolean isCritical,
191                            final ASN1OctetString value)
192         throws LDAPException
193  {
194    return new PasswordExpiredControl(oid, isCritical, value);
195  }
196
197
198
199  /**
200   * Extracts a password expired control from the provided result.
201   *
202   * @param  result  The result from which to retrieve the password expired
203   *                 control.
204   *
205   * @return  The password expired control contained in the provided result, or
206   *          {@code null} if the result did not contain a password expired
207   *          control.
208   *
209   * @throws  LDAPException  If a problem is encountered while attempting to
210   *                         decode the password expired control contained in
211   *                         the provided result.
212   */
213  public static PasswordExpiredControl get(final LDAPResult result)
214         throws LDAPException
215  {
216    final Control c = result.getResponseControl(PASSWORD_EXPIRED_OID);
217    if (c == null)
218    {
219      return null;
220    }
221
222    if (c instanceof PasswordExpiredControl)
223    {
224      return (PasswordExpiredControl) c;
225    }
226    else
227    {
228      return new PasswordExpiredControl(c.getOID(), c.isCritical(),
229           c.getValue());
230    }
231  }
232
233
234
235  /**
236   * Extracts a password expired control from the provided exception.
237   *
238   * @param  exception  The exception from which to retrieve the password
239   *                    expired control.
240   *
241   * @return  The password expired control contained in the provided exception,
242   *          or {@code null} if the exception did not contain a password
243   *          expired control.
244   *
245   * @throws  LDAPException  If a problem is encountered while attempting to
246   *                         decode the password expired control contained in
247   *                         the provided exception.
248   */
249  public static PasswordExpiredControl get(final LDAPException exception)
250         throws LDAPException
251  {
252    return get(exception.toLDAPResult());
253  }
254
255
256
257  /**
258   * {@inheritDoc}
259   */
260  @Override()
261  public String getControlName()
262  {
263    return INFO_CONTROL_NAME_PW_EXPIRED.get();
264  }
265
266
267
268  /**
269   * {@inheritDoc}
270   */
271  @Override()
272  public void toString(final StringBuilder buffer)
273  {
274    buffer.append("PasswordExpiredControl(isCritical=");
275    buffer.append(isCritical());
276    buffer.append(')');
277  }
278}