PasswordReminder
Password Reminders
Try the module located at http://github.com/gitpan/RT-Extension-ResetPassword
Older notes
I had a need to provide an easy way for unprivileged users to reset their password. I looked around the code base in 3.4.3 and only found a few TODO notes from JesseVincent. This is what I came up with.
(OleCraig notes: If you're looking for RT::User::SendPasswordEmail, the modified User overlay below is as close as you'll get as of 8/2006; the mention of that class in the ResetPassword function found in the stock 3.x.x User_Overlay.pm is an orphan call.)
/NoAuth/Reminder.html
Jesse provided a stub in [=$rt_dir/share/html/NoAuth/Reminder].
Copy below to [=/opt/rt3/local/html/NoAuth/Reminder.html]
I modified it to become this:
<& /Elements/Header, title => loc('Password Reminder') &> <DIV ALIGN=CENTER> % if ($Error) { <& /Elements/TitleBoxStart, title => loc('Error') &> <% $Error %> <& /Elements/TitleBoxEnd &> % } <& /Elements/TitleBoxStart, width=> "40%", titleright => loc("RT [_1]", $RT::VERSION), title => loc('Password Reminder') , contentbg=>"#cccccc" &> % if ($user_msg eq "Nothing selected.") { <FORM ACTION="<%$RT::WebPath%>/NoAuth/Reminder.html" METHOD=POST> <table> <tr> <td colspan=2> <p>Hello. If you are unable to remember your password, please enter your email address. A new one will be generated for you.</p> </td> </tr> <tr><td colspan=2> </td></tr> <tr> <td>Email address:</td> <td><input name="UserString"></td> </tr> </table> <& /Elements/Submit &> </FORM> %} else { <table> % if ($Users->Count == 1) { <tr><td>Hello % print $Users->First->EmailAddress; </td></tr> % my ($stat, $error) = $Users->First->ResetPassword(); <tr><td><%$error%></td></tr> <tr><td>Please <a href="<% $RT::WebURL %>">Login</a>.</td></tr> % } else { <tr><td><b>Could not find this user.</b></td></tr> % } </table> %} <& /Elements/TitleBoxEnd &> <& /Elements/Footer &> <%INIT> my ($user_msg, $Users); my $userobj = new RT::User($RT::SystemUser); if (! length $ARGS{'UserString'} ) { $user_msg = "Nothing selected."; } else { $Users = new RT::Users($RT::SystemUser); $Users->Limit(FIELD => 'EmailAddress', VALUE => $ARGS{'UserString'}, OPERATOR => '='); } </%INIT> <%ARGS> $UserString => undef $Error => undef </%ARGS>
/Elements/Login
Copy /opt/rt3/share/html/Elements/Login
to /opt/rt3/local/html/Elements/
and add below (only the + parts ;-) )
Next, I had to modify Elements/Login
to reference the new Reminder.html
.
--- /opt/rt3/share/html/Elements/Login Sun Mar 27 01:01:20 2005 +++ Elements/Login Fri Sep 30 16:19:54 2005 @@ -75,6 +75,9 @@ <TR><TD colspan=2 align=right> <input type=submit Value="<&|/l&>Login</&>"> </TD></TR> +<TR><TD colspan=2> +If you've forgotten your username or password, RT can <A +href="/NoAuth/Reminder.html">send you a reminder</a>.</TD></TR> </TABLE> %# Give callbacks a chance to add more control elements @@ -105,10 +108,6 @@ </DIV> <BR> -<!-- TODO: not yet implemented -If you've forgotten your username or password, RT can <A -href="/NoAuth/Reminder.html">send you a reminder</a>. ---> <BR> <BR> <BR>
User_Local.pm Overlay
Next, I had to create an overlay of [=RT::User::ResetPassword].
Save this as [=User_Local.pm] in /opt/rt3/local/lib/RT/
package RT::User; no warnings qw(redefine); use strict; sub ResetPassword { my $self = shift; unless ( $self->CurrentUserCanModify('Password') ) { return ( 0, $self->loc("Permission Denied") ); } my ( $status, $pass ) = $self->SetRandomPassword(); unless ($status) { return ( 0, "$pass" ); } my $template = RT::Template->new( $self->CurrentUser ); if ( $self->Privileged ) { $template->LoadGlobalTemplate('RT_PasswordChange_Privileged'); } else { $template->LoadGlobalTemplate('RT_PasswordChange_NonPrivileged'); } unless ( $template->Id ) { $template->LoadGlobalTemplate('RT_PasswordChange'); } unless ( $template->Id ) { $RT::Logger->crit( "$self tried to send " . $self->Name . " a password reminder " . "but couldn't find a password change template" ); } my ($result, $message) = $template->Parse( CurrentUser => $RT::SystemUser, TemplateObj => $template, Argument => $pass ); if ( !$result) { $RT::Logger->warn("User tried to reset password but template obj failed to load for " . $self->CurrentUser->Name); return ( 0, $self->loc("Sorry, something failed and I could not change your password")); } my $MIMEObj = $template->MIMEObj; $MIMEObj->head->set('To', $self->EmailAddress); $MIMEObj->head->set('From', $RT::CorrespondAddress); #$->SetHeader( 'To', $self->EmailAddress ); my $ret; if ( $RT::MailCommand eq 'sendmailpipe' ) { eval { open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" ) || die $!; print MAIL $MIMEObj->as_string; close(MAIL); }; if ($@) { $RT::Logger->crit("Could not send password reset. -" . $@ ); } else { $ret = 1; } } else { my @mailer_args = ($RT::MailCommand); local $ENV{MAILADDRESS}; if ( $RT::MailCommand eq 'sendmail' ) { push @mailer_args, split(/\s+/, $RT::SendmailArguments); } elsif ( $RT::MailCommand eq 'smtp' ) { $ENV{MAILADDRESS} = $RT::SMTPFrom || $MIMEObj->head->get('From'); push @mailer_args, ( Server => $RT::SMTPServer ); push @mailer_args, ( Debug => $RT::SMTPDebug ); } else { push @mailer_args, $RT::MailParams; } unless ( $MIMEObj->send(@mailer_args) ) { $RT::Logger->crit("Could not send password reset." ); return (0); } $ret = 1; } if ($ret) { return ( 1, $self->loc('New password notification sent') ); } else { return ( 0, $self->loc('Notification could not be sent') ); } } 1;
RT_PasswordChange Template
Finally, I had to create a template for my overlayed [=ResetPasswrd()].
* Name: RT_PasswordChange * Description: Notification of Password Change * Content: Subject: your {$rtname} account password reset Hello. Someone requested a password reminder for your account. Because we keep your password encrypted, we don't know what your previous password was. We have set a new password for your account. The only address that received this notice was the email on record for your account. The new password is "{$Argument}" without the double quotes. You may login to {$rtname} at {$RT::WebURL} at your convience. If you have further questions regarding this, please respond and a ticket will be opened regarding your request. Thank you, {$RT::CorrespondAddress}
Conclusion
This worked for me. YMMV. If you have questions about it, you can find me in #rt on irc.perl.org. I'm HCoyote
Here's a version of Reminder.html to look nice with RT 3.6.1
<& /Elements/Callback, %ARGS, _CallbackName => 'Header' &> <& /Elements/Header, Title => loc('Reset Your Password'), Focus => 'UserString' &> </div> <div id="body" class="login-body"> % if ($Error) { <&| "/Widgets/TitleBox", title => loc('Error'), hideable => 0 &> <% $Error %> </&> % } <& /Elements/Callback, %ARGS, _CallbackName => 'BeforeForm' &> <div id="login-box"> <&| "/Widgets/TitleBox", title => loc('Reset Your Password'), titleright => $RT::VERSION, hideable => 0 &> % if ($user_msg eq "Nothing selected.") { <form action="<%$RT::WebPath%>/NoAuth/Reminder.html" method="post"> <div class="input-row"> <p>Just enter your email address below, and a new one will be generated and sent to you.</p><p>If you have any problems, just <a href="http://www.suretecsystems.com/contact" title="Suretec Contact Details">call us</a>.</p> </div> <div class="input-row"> <span class="label">Email:</span> <span class="input"><input name="UserString"></span> </div> <div class="button-row"> <span class="input"><input type="submit" class="button" value="Reset" /></span> </div> </form> %} else { % if ($Users->Count == 1) { <div class="input-row">Hello % print $Users->First->EmailAddress; </div> % my ($stat, $error) = $Users->First->ResetPassword(); <div class="input-row"><%$error%></div> <div class="input-row">Please <a href="<% $RT::WebURL %>">Login</a>.</div> % } else { <div class="input-row"><b>Could not find this user.</b></div> % } % } </div><!-- #login-box --> </&> <& /Elements/Callback, %ARGS, _CallbackName => 'AfterForm' &> <& /Elements/Footer, Menu => 0 &> <%INIT> my ($user_msg, $Users); my $userobj = new RT::User($RT::SystemUser); if (! length $ARGS{'UserString'} ) { $user_msg = "Nothing selected."; } else { $Users = new RT::Users($RT::SystemUser); $Users->Limit(FIELD => 'EmailAddress', VALUE => $ARGS{'UserString'}, OPERATOR => '='); } </%INIT> <%ARGS> $UserString => undef $Error => undef </%ARGS>
For 3.8.4 we have used:
/NoAuth/Reminder.html
% $m->callback( %ARGS, CallbackName => 'Header' ); <& /Elements/Header, Title => loc('Reset Your Password'), Focus => 'UserString' &> </div> <div id="body" class="login-body"> % if ($Error) { <&| "/Widgets/TitleBox", title => loc('Error'), hideable => 0 &> <% $Error %> </&> % } % $m->callback( %ARGS, CallbackName => 'BeforeForm' ); <div id="login-box"> <&| "/Widgets/TitleBox", title => loc('Reset Your Password'), titleright => $RT::VERSION, hideable => 0 &> % if ($user_msg eq "Nothing selected.") { <form action="<%$RT::WebPath%>/NoAuth/Reminder.html" method="post"> <div class="input-row"> <p>If you have any problems, just <a href="http://www.suretecsystems.com/contact" title="Suretec Contact Details">call us</a>.</p> </div> <div class="input-row"> <span class="label">Email:</span> <span class="input"><input name="UserString"></span> </div> <div class="button-row"> <span class="input"><input type="submit" class="button" value="Reset" /></span> </div> <div class="input-row"> <a href="/">I've remembered, take me back.</a> </div> </form> %} else { % if ($Users->Count == 1) { <div class="input-row">Hello % print $Users->First->EmailAddress; </div> % my ($stat, $error) = $Users->First->ResetPassword(); <div class="input-row"><%$error%></div> <div class="input-row">Please <a href="<% $RT::WebURL %>">Login</a>.</div> % } else { <div class="input-row"><b>Could not find this user.</b></div> % } % } </div><!-- #login-box --> </&> % $m->callback( %ARGS, CallbackName => 'AfterForm' ); <& /Elements/Footer, Menu => 0 &> <%INIT> my ($user_msg, $Users); my $userobj = new RT::User($RT::SystemUser); if (! length $ARGS{'UserString'} ) { $user_msg = "Nothing selected."; } else { $Users = new RT::Users($RT::SystemUser); $Users->Limit(FIELD => 'EmailAddress', VALUE => $ARGS{'UserString'}, OPERATOR => '='); } </%INIT> <%ARGS> $UserString => undef $Error => undef </%ARGS>
Add this to the Login in local
/Elements/Login
<div class="input-row"> <a href="/NoAuth/Reminder.html">Forgotten password?</a> </div>