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>