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