001 /*
002 * Copyright 2008-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-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.extensions;
022
023
024
025 import java.util.ArrayList;
026
027 import com.unboundid.asn1.ASN1Element;
028 import com.unboundid.asn1.ASN1OctetString;
029 import com.unboundid.asn1.ASN1Sequence;
030 import com.unboundid.ldap.sdk.Control;
031 import com.unboundid.ldap.sdk.ExtendedRequest;
032 import com.unboundid.ldap.sdk.ExtendedResult;
033 import com.unboundid.ldap.sdk.LDAPConnection;
034 import com.unboundid.ldap.sdk.LDAPException;
035 import com.unboundid.ldap.sdk.ResultCode;
036 import com.unboundid.util.NotMutable;
037 import com.unboundid.util.ThreadSafety;
038 import com.unboundid.util.ThreadSafetyLevel;
039
040 import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
041 import static com.unboundid.util.Debug.*;
042 import static com.unboundid.util.StaticUtils.*;
043
044
045
046 /**
047 * This class provides an implementation of the LDAP password modify extended
048 * request as defined in
049 * <A HREF="http://www.ietf.org/rfc/rfc3062.txt">RFC 3062</A>. It may be used
050 * to change the password for a user in the directory, and provides the ability
051 * to specify the current password for verification. It also offers the ability
052 * to request that the server generate a new password for the user.
053 * <BR><BR>
054 * The elements of a password modify extended request include:
055 * <UL>
056 * <LI>{@code userIdentity} -- This specifies the user for which to change the
057 * password. It should generally be the DN for the target user (although
058 * the specification does indicate that some servers may accept other
059 * values). If no value is provided, then the server will attempt to
060 * change the password for the currently-authenticated user.</LI>
061 * <LI>{@code oldPassword} -- This specifies the current password for the
062 * user. Some servers may require that the old password be provided when
063 * a user is changing his or her own password as an extra level of
064 * verification, but it is generally not necessary when an administrator
065 * is resetting the password for another user.</LI>
066 * <LI>{@code newPassword} -- This specifies the new password to use for the
067 * user. If it is not provided, then the server may attempt to generate a
068 * new password for the user, and in that case it will be included in the
069 * {@code generatedPassword} field of the corresponding
070 * {@link PasswordModifyExtendedResult}. Note that some servers may not
071 * support generating a new password, in which case the client will always
072 * be required to provide it.</LI>
073 * </UL>
074 * <H2>Example</H2>
075 * The following example demonstrates the use of the password modify extended
076 * operation to change the password for user
077 * "uid=john.doe,ou=People,dc=example,dc=com". Neither the current password nor
078 * a new password will be provided, so the server will generate a new password
079 * for the user.
080 * <PRE>
081 * PasswordModifyExtendedRequest passwordModifyRequest =
082 * new PasswordModifyExtendedRequest(
083 * "uid=john.doe,ou=People,dc=example,dc=com", null, null);
084 * PasswordModifyExtendedResult passwordModifyResult =
085 * (PasswordModifyExtendedResult)
086 * connection.processExtendedOperation(passwordModifyRequest);
087 *
088 * // NOTE: The processExtendedOperation method will only throw an exception
089 * // if a problem occurs while trying to send the request or read the
090 * // response. It will not throw an exception because of a non-success
091 * // response.
092 *
093 * if (passwordModifyResult.getResultCode() == ResultCode.SUCCESS)
094 * {
095 * System.out.println("The password change was successful.");
096 * System.out.println("The new password for the user is " +
097 * passwordModifyResult.getGeneratedPassword());
098 * }
099 * else
100 * {
101 * System.err.println("An error occurred while attempting to process " +
102 * "the password modify extended request.");
103 * }
104 * </PRE>
105 */
106 @NotMutable()
107 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
108 public final class PasswordModifyExtendedRequest
109 extends ExtendedRequest
110 {
111 /**
112 * The OID (1.3.6.1.4.1.4203.1.11.1) for the password modify extended request.
113 */
114 public static final String PASSWORD_MODIFY_REQUEST_OID =
115 "1.3.6.1.4.1.4203.1.11.1";
116
117
118
119 /**
120 * The BER type for the user identity element.
121 */
122 private static final byte TYPE_USER_IDENTITY = (byte) 0x80;
123
124
125
126 /**
127 * The BER type for the old password element.
128 */
129 private static final byte TYPE_OLD_PASSWORD = (byte) 0x81;
130
131
132
133 /**
134 * The BER type for the new password element.
135 */
136 private static final byte TYPE_NEW_PASSWORD = (byte) 0x82;
137
138
139
140 /**
141 * The serial version UID for this serializable class.
142 */
143 private static final long serialVersionUID = 4965048727456933570L;
144
145
146
147 // The old password for this request.
148 private final ASN1OctetString oldPassword;
149
150 // The new password for this request.
151 private final ASN1OctetString newPassword;
152
153 // The user identity string for this request.
154 private final String userIdentity;
155
156
157
158 /**
159 * Creates a new password modify extended request that will attempt to change
160 * the password of the currently-authenticated user.
161 *
162 * @param newPassword The new password for the user. It may be {@code null}
163 * if the new password should be generated by the
164 * directory server.
165 */
166 public PasswordModifyExtendedRequest(final String newPassword)
167 {
168 this(null, null, newPassword, null);
169 }
170
171
172
173 /**
174 * Creates a new password modify extended request that will attempt to change
175 * the password of the currently-authenticated user.
176 *
177 * @param newPassword The new password for the user. It may be {@code null}
178 * if the new password should be generated by the
179 * directory server.
180 */
181 public PasswordModifyExtendedRequest(final byte[] newPassword)
182 {
183 this(null, null, newPassword, null);
184 }
185
186
187
188 /**
189 * Creates a new password modify extended request that will attempt to change
190 * the password of the currently-authenticated user.
191 *
192 * @param oldPassword The current password for the user. It may be
193 * {@code null} if the directory server does not require
194 * the user's current password for self changes.
195 * @param newPassword The new password for the user. It may be {@code null}
196 * if the new password should be generated by the
197 * directory server.
198 */
199 public PasswordModifyExtendedRequest(final String oldPassword,
200 final String newPassword)
201 {
202 this(null, oldPassword, newPassword, null);
203 }
204
205
206
207 /**
208 * Creates a new password modify extended request that will attempt to change
209 * the password of the currently-authenticated user.
210 *
211 * @param oldPassword The current password for the user. It may be
212 * {@code null} if the directory server does not require
213 * the user's current password for self changes.
214 * @param newPassword The new password for the user. It may be {@code null}
215 * if the new password should be generated by the
216 * directory server.
217 */
218 public PasswordModifyExtendedRequest(final byte[] oldPassword,
219 final byte[] newPassword)
220 {
221 this(null, oldPassword, newPassword, null);
222 }
223
224
225
226 /**
227 * Creates a new password modify extended request that will attempt to change
228 * the password for the specified user.
229 *
230 * @param userIdentity The string that identifies the user whose password
231 * should be changed. It may or may not be a DN, but if
232 * it is not a DN, then the directory server must be
233 * able to identify the appropriate user from the
234 * provided identifier. It may be {@code null} to
235 * indicate that the password change should be for the
236 * currently-authenticated user.
237 * @param oldPassword The current password for the user. It may be
238 * {@code null} if the directory server does not require
239 * the user's current password for self changes.
240 * @param newPassword The new password for the user. It may be
241 * {@code null} if the new password should be generated
242 * by the directory server.
243 */
244 public PasswordModifyExtendedRequest(final String userIdentity,
245 final String oldPassword,
246 final String newPassword)
247 {
248 this(userIdentity, oldPassword, newPassword, null);
249 }
250
251
252
253 /**
254 * Creates a new password modify extended request that will attempt to change
255 * the password for the specified user.
256 *
257 * @param userIdentity The string that identifies the user whose password
258 * should be changed. It may or may not be a DN, but if
259 * it is not a DN, then the directory server must be
260 * able to identify the appropriate user from the
261 * provided identifier. It may be {@code null} to
262 * indicate that the password change should be for the
263 * currently-authenticated user.
264 * @param oldPassword The current password for the user. It may be
265 * {@code null} if the directory server does not require
266 * the user's current password for self changes.
267 * @param newPassword The new password for the user. It may be
268 * {@code null} if the new password should be generated
269 * by the directory server.
270 */
271 public PasswordModifyExtendedRequest(final String userIdentity,
272 final byte[] oldPassword,
273 final byte[] newPassword)
274 {
275 this(userIdentity, oldPassword, newPassword, null);
276 }
277
278
279
280 /**
281 * Creates a new password modify extended request that will attempt to change
282 * the password for the specified user.
283 *
284 * @param userIdentity The string that identifies the user whose password
285 * should be changed. It may or may not be a DN, but if
286 * it is not a DN, then the directory server must be
287 * able to identify the appropriate user from the
288 * provided identifier. It may be {@code null} to
289 * indicate that the password change should be for the
290 * currently-authenticated user.
291 * @param oldPassword The current password for the user. It may be
292 * {@code null} if the directory server does not require
293 * the user's current password for self changes.
294 * @param newPassword The new password for the user. It may be
295 * {@code null} if the new password should be generated
296 * by the directory server.
297 * @param controls The set of controls to include in the request.
298 */
299 public PasswordModifyExtendedRequest(final String userIdentity,
300 final String oldPassword,
301 final String newPassword,
302 final Control[] controls)
303 {
304 super(PASSWORD_MODIFY_REQUEST_OID,
305 encodeValue(userIdentity, oldPassword, newPassword), controls);
306
307 this.userIdentity = userIdentity;
308
309 if (oldPassword == null)
310 {
311 this.oldPassword = null;
312 }
313 else
314 {
315 this.oldPassword = new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword);
316 }
317
318 if (newPassword == null)
319 {
320 this.newPassword = null;
321 }
322 else
323 {
324 this.newPassword = new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword);
325 }
326 }
327
328
329
330 /**
331 * Creates a new password modify extended request that will attempt to change
332 * the password for the specified user.
333 *
334 * @param userIdentity The string that identifies the user whose password
335 * should be changed. It may or may not be a DN, but if
336 * it is not a DN, then the directory server must be
337 * able to identify the appropriate user from the
338 * provided identifier. It may be {@code null} to
339 * indicate that the password change should be for the
340 * currently-authenticated user.
341 * @param oldPassword The current password for the user. It may be
342 * {@code null} if the directory server does not require
343 * the user's current password for self changes.
344 * @param newPassword The new password for the user. It may be
345 * {@code null} if the new password should be generated
346 * by the directory server.
347 * @param controls The set of controls to include in the request.
348 */
349 public PasswordModifyExtendedRequest(final String userIdentity,
350 final byte[] oldPassword,
351 final byte[] newPassword,
352 final Control[] controls)
353 {
354 super(PASSWORD_MODIFY_REQUEST_OID,
355 encodeValue(userIdentity, oldPassword, newPassword), controls);
356
357 this.userIdentity = userIdentity;
358
359 if (oldPassword == null)
360 {
361 this.oldPassword = null;
362 }
363 else
364 {
365 this.oldPassword = new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword);
366 }
367
368 if (newPassword == null)
369 {
370 this.newPassword = null;
371 }
372 else
373 {
374 this.newPassword = new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword);
375 }
376 }
377
378
379
380 /**
381 * Creates a new password modify extended request from the provided generic
382 * extended request.
383 *
384 * @param extendedRequest The generic extended request to use to create this
385 * password modify extended request.
386 *
387 * @throws LDAPException If a problem occurs while decoding the request.
388 */
389 public PasswordModifyExtendedRequest(final ExtendedRequest extendedRequest)
390 throws LDAPException
391 {
392 super(extendedRequest);
393
394 final ASN1OctetString value = extendedRequest.getValue();
395 if (value == null)
396 {
397 throw new LDAPException(ResultCode.DECODING_ERROR,
398 ERR_PW_MODIFY_REQUEST_NO_VALUE.get());
399 }
400
401 try
402 {
403 ASN1OctetString oldPW = null;
404 ASN1OctetString newPW = null;
405 String userID = null;
406
407 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
408 final ASN1Element[] elements =
409 ASN1Sequence.decodeAsSequence(valueElement).elements();
410 for (final ASN1Element e : elements)
411 {
412 switch (e.getType())
413 {
414 case TYPE_USER_IDENTITY:
415 userID = ASN1OctetString.decodeAsOctetString(e).stringValue();
416 break;
417
418 case TYPE_OLD_PASSWORD:
419 oldPW = ASN1OctetString.decodeAsOctetString(e);
420 break;
421
422 case TYPE_NEW_PASSWORD:
423 newPW = ASN1OctetString.decodeAsOctetString(e);
424 break;
425
426 default:
427 throw new LDAPException(ResultCode.DECODING_ERROR,
428 ERR_PW_MODIFY_REQUEST_INVALID_TYPE.get(
429 toHex(e.getType())));
430 }
431 }
432
433 userIdentity = userID;
434 oldPassword = oldPW;
435 newPassword = newPW;
436 }
437 catch (LDAPException le)
438 {
439 debugException(le);
440 throw le;
441 }
442 catch (Exception e)
443 {
444 debugException(e);
445 throw new LDAPException(ResultCode.DECODING_ERROR,
446 ERR_PW_MODIFY_REQUEST_CANNOT_DECODE.get(e), e);
447 }
448 }
449
450
451
452 /**
453 * Encodes the provided information into an ASN.1 octet string suitable for
454 * use as the value of this extended request.
455 *
456 * @param userIdentity The string that identifies the user whose password
457 * should be changed. It may or may not be a DN, but if
458 * it is not a DN, then the directory server must be
459 * able to identify the appropriate user from the
460 * provided identifier. It may be {@code null} to
461 * indicate that the password change should be for the
462 * currently-authenticated user.
463 * @param oldPassword The current password for the user. It may be
464 * {@code null} if the directory server does not require
465 * the user's current password for self changes.
466 * @param newPassword The new password for the user. It may be
467 * {@code null} if the new password should be generated
468 * by the directory server.
469 *
470 * @return The ASN.1 octet string containing the encoded value.
471 */
472 private static ASN1OctetString encodeValue(final String userIdentity,
473 final String oldPassword,
474 final String newPassword)
475 {
476 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
477
478 if (userIdentity != null)
479 {
480 elements.add(new ASN1OctetString(TYPE_USER_IDENTITY, userIdentity));
481 }
482
483 if (oldPassword != null)
484 {
485 elements.add(new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword));
486 }
487
488 if (newPassword != null)
489 {
490 elements.add(new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword));
491 }
492
493 return new ASN1OctetString(new ASN1Sequence(elements).encode());
494 }
495
496
497
498 /**
499 * Encodes the provided information into an ASN.1 octet string suitable for
500 * use as the value of this extended request.
501 *
502 * @param userIdentity The string that identifies the user whose password
503 * should be changed. It may or may not be a DN, but if
504 * it is not a DN, then the directory server must be
505 * able to identify the appropriate user from the
506 * provided identifier. It may be {@code null} to
507 * indicate that the password change should be for the
508 * currently-authenticated user.
509 * @param oldPassword The current password for the user. It may be
510 * {@code null} if the directory server does not require
511 * the user's current password for self changes.
512 * @param newPassword The new password for the user. It may be
513 * {@code null} if the new password should be generated
514 * by the directory server.
515 *
516 * @return The ASN.1 octet string containing the encoded value.
517 */
518 private static ASN1OctetString encodeValue(final String userIdentity,
519 final byte[] oldPassword,
520 final byte[] newPassword)
521 {
522 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
523
524 if (userIdentity != null)
525 {
526 elements.add(new ASN1OctetString(TYPE_USER_IDENTITY, userIdentity));
527 }
528
529 if (oldPassword != null)
530 {
531 elements.add(new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword));
532 }
533
534 if (newPassword != null)
535 {
536 elements.add(new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword));
537 }
538
539 return new ASN1OctetString(new ASN1Sequence(elements).encode());
540 }
541
542
543
544 /**
545 * Retrieves the user identity for this request, if available.
546 *
547 * @return The user identity for this request, or {@code null} if the
548 * password change should target the currently-authenticated user.
549 */
550 public String getUserIdentity()
551 {
552 return userIdentity;
553 }
554
555
556
557 /**
558 * Retrieves the string representation of the old password for this request,
559 * if available.
560 *
561 * @return The string representation of the old password for this request, or
562 * {@code null} if it was not provided.
563 */
564 public String getOldPassword()
565 {
566 if (oldPassword == null)
567 {
568 return null;
569 }
570 else
571 {
572 return oldPassword.stringValue();
573 }
574 }
575
576
577
578 /**
579 * Retrieves the binary representation of the old password for this request,
580 * if available.
581 *
582 * @return The binary representation of the old password for this request, or
583 * {@code null} if it was not provided.
584 */
585 public byte[] getOldPasswordBytes()
586 {
587 if (oldPassword == null)
588 {
589 return null;
590 }
591 else
592 {
593 return oldPassword.getValue();
594 }
595 }
596
597
598
599 /**
600 * Retrieves the raw old password for this request, if available.
601 *
602 * @return The raw old password for this request, or {@code null} if it was
603 * not provided.
604 */
605 public ASN1OctetString getRawOldPassword()
606 {
607 return oldPassword;
608 }
609
610
611
612 /**
613 * Retrieves the string representation of the new password for this request,
614 * if available.
615 *
616 * @return The string representation of the new password for this request, or
617 * {@code null} if it was not provided.
618 */
619 public String getNewPassword()
620 {
621 if (newPassword == null)
622 {
623 return null;
624 }
625 else
626 {
627 return newPassword.stringValue();
628 }
629 }
630
631
632
633 /**
634 * Retrieves the binary representation of the new password for this request,
635 * if available.
636 *
637 * @return The binary representation of the new password for this request, or
638 * {@code null} if it was not provided.
639 */
640 public byte[] getNewPasswordBytes()
641 {
642 if (newPassword == null)
643 {
644 return null;
645 }
646 else
647 {
648 return newPassword.getValue();
649 }
650 }
651
652
653
654 /**
655 * Retrieves the raw new password for this request, if available.
656 *
657 * @return The raw new password for this request, or {@code null} if it was
658 * not provided.
659 */
660 public ASN1OctetString getRawNewPassword()
661 {
662 return newPassword;
663 }
664
665
666
667 /**
668 * {@inheritDoc}
669 */
670 @Override()
671 public PasswordModifyExtendedResult process(final LDAPConnection connection,
672 final int depth)
673 throws LDAPException
674 {
675 final ExtendedResult extendedResponse = super.process(connection, depth);
676 return new PasswordModifyExtendedResult(extendedResponse);
677 }
678
679
680
681 /**
682 * {@inheritDoc}
683 */
684 @Override()
685 public PasswordModifyExtendedRequest duplicate()
686 {
687 return duplicate(getControls());
688 }
689
690
691
692 /**
693 * {@inheritDoc}
694 */
695 @Override()
696 public PasswordModifyExtendedRequest duplicate(final Control[] controls)
697 {
698 final byte[] oldPWBytes =
699 (oldPassword == null) ? null : oldPassword.getValue();
700 final byte[] newPWBytes =
701 (newPassword == null) ? null : newPassword.getValue();
702
703 final PasswordModifyExtendedRequest r =
704 new PasswordModifyExtendedRequest(userIdentity, oldPWBytes,
705 newPWBytes, controls);
706 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
707 return r;
708 }
709
710
711
712 /**
713 * {@inheritDoc}
714 */
715 @Override()
716 public String getExtendedRequestName()
717 {
718 return INFO_EXTENDED_REQUEST_NAME_PASSWORD_MODIFY.get();
719 }
720
721
722
723 /**
724 * {@inheritDoc}
725 */
726 @Override()
727 public void toString(final StringBuilder buffer)
728 {
729 buffer.append("PasswordModifyExtendedRequest(");
730
731 boolean dataAdded = false;
732
733 if (userIdentity != null)
734 {
735 buffer.append("userIdentity='");
736 buffer.append(userIdentity);
737 buffer.append('\'');
738 dataAdded = true;
739 }
740
741 if (oldPassword != null)
742 {
743 if (dataAdded)
744 {
745 buffer.append(", ");
746 }
747
748 buffer.append("oldPassword='");
749 buffer.append(oldPassword.stringValue());
750 buffer.append('\'');
751 dataAdded = true;
752 }
753
754 if (newPassword != null)
755 {
756 if (dataAdded)
757 {
758 buffer.append(", ");
759 }
760
761 buffer.append("newPassword='");
762 buffer.append(newPassword.stringValue());
763 buffer.append('\'');
764 dataAdded = true;
765 }
766
767 final Control[] controls = getControls();
768 if (controls.length > 0)
769 {
770 if (dataAdded)
771 {
772 buffer.append(", ");
773 }
774
775 buffer.append("controls={");
776 for (int i=0; i < controls.length; i++)
777 {
778 if (i > 0)
779 {
780 buffer.append(", ");
781 }
782
783 buffer.append(controls[i]);
784 }
785 buffer.append('}');
786 }
787
788 buffer.append(')');
789 }
790 }