001 /*
002 * Copyright 2007-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.controls;
022
023
024
025 import java.util.ArrayList;
026
027 import com.unboundid.asn1.ASN1Element;
028 import com.unboundid.asn1.ASN1Enumerated;
029 import com.unboundid.asn1.ASN1Exception;
030 import com.unboundid.asn1.ASN1Integer;
031 import com.unboundid.asn1.ASN1OctetString;
032 import com.unboundid.asn1.ASN1Sequence;
033 import com.unboundid.ldap.sdk.Control;
034 import com.unboundid.ldap.sdk.DecodeableControl;
035 import com.unboundid.ldap.sdk.LDAPException;
036 import com.unboundid.ldap.sdk.LDAPResult;
037 import com.unboundid.ldap.sdk.ResultCode;
038 import com.unboundid.util.NotMutable;
039 import com.unboundid.util.ThreadSafety;
040 import com.unboundid.util.ThreadSafetyLevel;
041
042 import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
043 import static com.unboundid.util.Debug.*;
044 import static com.unboundid.util.StaticUtils.*;
045
046
047
048 /**
049 * <BLOCKQUOTE>
050 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID
051 * LDAP SDK for Java. It is not available for use in applications that
052 * include only the Standard Edition of the LDAP SDK, and is not supported for
053 * use in conjunction with non-UnboundID products.
054 * </BLOCKQUOTE>
055 * This class provides an implementation of the password policy response control
056 * as described in draft-behera-ldap-password-policy. It may be used to provide
057 * information related to a user's password policy. It may include at most one
058 * warning from the set of {@link PasswordPolicyWarningType} values and at most
059 * one error from the set of {@link PasswordPolicyErrorType} values. See the
060 * documentation for those classes for more information on the information that
061 * may be included. See the {@link PasswordPolicyRequestControl} documentation
062 * for an example that demonstrates the use of the password policy request and
063 * response controls.
064 */
065 @NotMutable()
066 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
067 public final class PasswordPolicyResponseControl
068 extends Control
069 implements DecodeableControl
070 {
071 /**
072 * The OID (1.3.6.1.4.1.42.2.27.8.5.1) for the password policy response
073 * control.
074 */
075 public static final String PASSWORD_POLICY_RESPONSE_OID =
076 "1.3.6.1.4.1.42.2.27.8.5.1";
077
078
079
080 /**
081 * The BER type for the password policy warning element.
082 */
083 private static final byte TYPE_WARNING = (byte) 0xA0;
084
085
086
087 /**
088 * The BER type for the password policy error element.
089 */
090 private static final byte TYPE_ERROR = (byte) 0x81;
091
092
093
094 /**
095 * The BER type for the "time before expiration" warning element.
096 */
097 private static final byte TYPE_TIME_BEFORE_EXPIRATION = (byte) 0x80;
098
099
100
101 /**
102 * The BER type for the "grace logins remaining" warning element.
103 */
104 private static final byte TYPE_GRACE_LOGINS_REMAINING = (byte) 0x81;
105
106
107
108 /**
109 * The serial version UID for this serializable class.
110 */
111 private static final long serialVersionUID = 1835830253434331833L;
112
113
114
115 // The password policy warning value, if applicable.
116 private final int warningValue;
117
118 // The password policy error type, if applicable.
119 private final PasswordPolicyErrorType errorType;
120
121 // The password policy warning type, if applicable.
122 private final PasswordPolicyWarningType warningType;
123
124
125
126 /**
127 * Creates a new empty control instance that is intended to be used only for
128 * decoding controls via the {@code DecodeableControl} interface.
129 */
130 PasswordPolicyResponseControl()
131 {
132 warningType = null;
133 errorType = null;
134 warningValue = -1;
135 }
136
137
138
139 /**
140 * Creates a new password policy response control with the provided
141 * information. It will not be critical.
142 *
143 * @param warningType The password policy warning type for this response
144 * control, or {@code null} if there should be no
145 * warning type.
146 * @param warningValue The value for the password policy warning type, or -1
147 * if there is no warning type.
148 * @param errorType The password policy error type for this response
149 * control, or {@code null} if there should be no error
150 * type.
151 */
152 public PasswordPolicyResponseControl(
153 final PasswordPolicyWarningType warningType,
154 final int warningValue, final PasswordPolicyErrorType errorType)
155 {
156 this(warningType, warningValue, errorType, false);
157 }
158
159
160
161 /**
162 * Creates a new password policy response control with the provided
163 * information.
164 *
165 * @param warningType The password policy warning type for this response
166 * control, or {@code null} if there should be no
167 * warning type.
168 * @param warningValue The value for the password policy warning type, or -1
169 * if there is no warning type.
170 * @param errorType The password policy error type for this response
171 * control, or {@code null} if there should be no error
172 * type.
173 * @param isCritical Indicates whether this control should be marked
174 * critical.
175 */
176 public PasswordPolicyResponseControl(
177 final PasswordPolicyWarningType warningType,
178 final int warningValue, final PasswordPolicyErrorType errorType,
179 final boolean isCritical)
180 {
181 super(PASSWORD_POLICY_RESPONSE_OID, isCritical,
182 encodeValue(warningType, warningValue, errorType));
183
184 this.warningType = warningType;
185 this.errorType = errorType;
186
187 if (warningType == null)
188 {
189 this.warningValue = -1;
190 }
191 else
192 {
193 this.warningValue = warningValue;
194 }
195 }
196
197
198
199 /**
200 * Creates a new password policy response control with the provided
201 * information.
202 *
203 * @param oid The OID for the control.
204 * @param isCritical Indicates whether the control should be marked
205 * critical.
206 * @param value The encoded value for the control. This may be
207 * {@code null} if no value was provided.
208 *
209 * @throws LDAPException If the provided control cannot be decoded as a
210 * password policy response control.
211 */
212 public PasswordPolicyResponseControl(final String oid,
213 final boolean isCritical,
214 final ASN1OctetString value)
215 throws LDAPException
216 {
217 super(oid, isCritical, value);
218
219 if (value == null)
220 {
221 throw new LDAPException(ResultCode.DECODING_ERROR,
222 ERR_PWP_RESPONSE_NO_VALUE.get());
223 }
224
225 final ASN1Sequence valueSequence;
226 try
227 {
228 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
229 valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
230 }
231 catch (ASN1Exception ae)
232 {
233 debugException(ae);
234 throw new LDAPException(ResultCode.DECODING_ERROR,
235 ERR_PWP_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae);
236 }
237
238 final ASN1Element[] valueElements = valueSequence.elements();
239 if (valueElements.length > 2)
240 {
241 throw new LDAPException(ResultCode.DECODING_ERROR,
242 ERR_PWP_RESPONSE_INVALID_ELEMENT_COUNT.get(
243 valueElements.length));
244 }
245
246 int wv = -1;
247 PasswordPolicyErrorType et = null;
248 PasswordPolicyWarningType wt = null;
249 for (final ASN1Element e : valueElements)
250 {
251 switch (e.getType())
252 {
253 case TYPE_WARNING:
254 if (wt == null)
255 {
256 try
257 {
258 final ASN1Element warningElement =
259 ASN1Element.decode(e.getValue());
260 wv = ASN1Integer.decodeAsInteger(warningElement).intValue();
261 switch (warningElement.getType())
262 {
263 case TYPE_TIME_BEFORE_EXPIRATION:
264 wt = PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION;
265 break;
266
267 case TYPE_GRACE_LOGINS_REMAINING:
268 wt = PasswordPolicyWarningType.GRACE_LOGINS_REMAINING;
269 break;
270
271 default:
272 throw new LDAPException(ResultCode.DECODING_ERROR,
273 ERR_PWP_RESPONSE_INVALID_WARNING_TYPE.get(
274 toHex(warningElement.getType())));
275 }
276 }
277 catch (ASN1Exception ae)
278 {
279 debugException(ae);
280 throw new LDAPException(ResultCode.DECODING_ERROR,
281 ERR_PWP_RESPONSE_CANNOT_DECODE_WARNING.get(ae),
282 ae);
283 }
284 }
285 else
286 {
287 throw new LDAPException(ResultCode.DECODING_ERROR,
288 ERR_PWP_RESPONSE_MULTIPLE_WARNING.get());
289 }
290 break;
291
292 case TYPE_ERROR:
293 if (et == null)
294 {
295 try
296 {
297 final ASN1Enumerated errorElement =
298 ASN1Enumerated.decodeAsEnumerated(e);
299 et = PasswordPolicyErrorType.valueOf(errorElement.intValue());
300 if (et == null)
301 {
302 throw new LDAPException(ResultCode.DECODING_ERROR,
303 ERR_PWP_RESPONSE_INVALID_ERROR_TYPE.get(
304 errorElement.intValue()));
305 }
306 }
307 catch (ASN1Exception ae)
308 {
309 debugException(ae);
310 throw new LDAPException(ResultCode.DECODING_ERROR,
311 ERR_PWP_RESPONSE_CANNOT_DECODE_ERROR.get(ae), ae);
312 }
313 }
314 else
315 {
316 throw new LDAPException(ResultCode.DECODING_ERROR,
317 ERR_PWP_RESPONSE_MULTIPLE_ERROR.get());
318 }
319 break;
320
321 default:
322 throw new LDAPException(ResultCode.DECODING_ERROR,
323 ERR_PWP_RESPONSE_INVALID_TYPE.get(
324 toHex(e.getType())));
325 }
326 }
327
328 warningType = wt;
329 warningValue = wv;
330 errorType = et;
331 }
332
333
334
335 /**
336 * {@inheritDoc}
337 */
338 public PasswordPolicyResponseControl
339 decodeControl(final String oid, final boolean isCritical,
340 final ASN1OctetString value)
341 throws LDAPException
342 {
343 return new PasswordPolicyResponseControl(oid, isCritical, value);
344 }
345
346
347
348 /**
349 * Extracts a password policy response control from the provided result.
350 *
351 * @param result The result from which to retrieve the password policy
352 * response control.
353 *
354 * @return The password policy response control contained in the provided
355 * result, or {@code null} if the result did not contain a password
356 * policy response control.
357 *
358 * @throws LDAPException If a problem is encountered while attempting to
359 * decode the password policy response control
360 * contained in the provided result.
361 */
362 public static PasswordPolicyResponseControl get(final LDAPResult result)
363 throws LDAPException
364 {
365 final Control c = result.getResponseControl(PASSWORD_POLICY_RESPONSE_OID);
366 if (c == null)
367 {
368 return null;
369 }
370
371 if (c instanceof PasswordPolicyResponseControl)
372 {
373 return (PasswordPolicyResponseControl) c;
374 }
375 else
376 {
377 return new PasswordPolicyResponseControl(c.getOID(), c.isCritical(),
378 c.getValue());
379 }
380 }
381
382
383
384 /**
385 * Encodes the provided information as appropriate for use as the value of a
386 * password policy response control.
387 *
388 * @param warningType The warning type to use for the warning element, or
389 * {@code null} if there is not to be a warning element.
390 * @param warningValue The value to use for the warning element.
391 * @param errorType The error type to use for the error element, or
392 * {@code null} if there is not to be an error element.
393 *
394 * @return The ASN.1 octet string containing the encoded control value.
395 */
396 private static ASN1OctetString
397 encodeValue(final PasswordPolicyWarningType warningType,
398 final int warningValue,
399 final PasswordPolicyErrorType errorType)
400 {
401 final ArrayList<ASN1Element> valueElements = new ArrayList<ASN1Element>(2);
402
403 if (warningType != null)
404 {
405 switch (warningType)
406 {
407 case TIME_BEFORE_EXPIRATION:
408 valueElements.add(new ASN1Element(TYPE_WARNING,
409 new ASN1Integer(TYPE_TIME_BEFORE_EXPIRATION,
410 warningValue).encode()));
411 break;
412
413 case GRACE_LOGINS_REMAINING:
414 valueElements.add(new ASN1Element(TYPE_WARNING,
415 new ASN1Integer(TYPE_GRACE_LOGINS_REMAINING,
416 warningValue).encode()));
417 break;
418 }
419 }
420
421 if (errorType != null)
422 {
423 valueElements.add(new ASN1Enumerated(TYPE_ERROR, errorType.intValue()));
424 }
425
426 return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
427 }
428
429
430
431 /**
432 * Retrieves the warning type for this password policy response control, if
433 * available.
434 *
435 * @return The warning type for this password policy response control, or
436 * {@code null} if there is no warning type.
437 */
438 public PasswordPolicyWarningType getWarningType()
439 {
440 return warningType;
441 }
442
443
444
445 /**
446 * Retrieves the warning value for this password policy response control, if
447 * available.
448 *
449 * @return The warning value for this password policy response control, or -1
450 * if there is no warning type.
451 */
452 public int getWarningValue()
453 {
454 return warningValue;
455 }
456
457
458
459 /**
460 * Retrieves the error type for this password policy response control, if
461 * available.
462 *
463 * @return The error type for this password policy response control, or
464 * {@code null} if there is no error type.
465 */
466 public PasswordPolicyErrorType getErrorType()
467 {
468 return errorType;
469 }
470
471
472
473 /**
474 * {@inheritDoc}
475 */
476 @Override()
477 public String getControlName()
478 {
479 return INFO_CONTROL_NAME_PW_POLICY_RESPONSE.get();
480 }
481
482
483
484 /**
485 * {@inheritDoc}
486 */
487 @Override()
488 public void toString(final StringBuilder buffer)
489 {
490 boolean elementAdded = false;
491
492 buffer.append("PasswordPolicyResponseControl(");
493
494 if (warningType != null)
495 {
496 buffer.append("warningType='");
497 buffer.append(warningType.getName());
498 buffer.append("', warningValue=");
499 buffer.append(warningValue);
500 elementAdded = true;
501 }
502
503 if (errorType != null)
504 {
505 if (elementAdded)
506 {
507 buffer.append(", ");
508 }
509
510 buffer.append("errorType='");
511 buffer.append(errorType.getName());
512 buffer.append('\'');
513 elementAdded = true;
514 }
515
516 if (elementAdded)
517 {
518 buffer.append(", ");
519 }
520
521 buffer.append("isCritical=");
522 buffer.append(isCritical());
523 buffer.append(')');
524 }
525 }