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