001 /*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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 * // 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)
118 public 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 }