App-ZofCMS
view release on metacpan or search on metacpan
lib/App/ZofCMS/Plugin/UserLogin/ForgotPassword.pm view on Meta::CPAN
package App::ZofCMS::Plugin::UserLogin::ForgotPassword;
use warnings;
use strict;
our $VERSION = '1.001008'; # VERSION
use base 'App::ZofCMS::Plugin::Base';
use DBI;
use Digest::MD5 (qw/md5_hex/);
use HTML::Template;
use MIME::Lite;
sub _key { 'plug_user_login_forgot_password' }
sub _defaults {
return (
dsn => "DBI:mysql:database=test;host=localhost",
user => '',
pass => undef,
opt => { RaiseError => 1, AutoCommit => 1 },
users_table => 'users',
code_table => 'users_forgot_password',
q_code => 'pulfp_code',
max_abuse => '5:10:60', # 5 min intervals, max 10 attempts per 60 min.
min_pass => 6,
code_expiry => 24*60*60, # 1 day
code_length => 6,
use_stage_indicators => 1,
subject => 'Password Reset',
login_page => '/',
button_send_link => q|<input type="submit" class="input_submit"|
. q| value="Send password">|,
button_change_pass => q|<input type="submit" class="input_submit"|
. q| value="Change password">|,
# email_link => undef, # this will be guessed
# from => undef,
# email_template => undef, # use plugin's default template
# no_run => undef
# create_table => undef,
# mime_lite_params => undef,
# email => undef,
);
}
sub _do {
my ( $self, $conf, $t, $q, $config ) = @_;
return
if $conf->{no_run};
$self->{Q_PAGE} = ( $q->{dir} || '' ) . ( $q->{page} || '' );
$self->{CONF} = $conf;
$self->{Q} = $q;
$self->{T} = $t;
create_code_table( $conf )
if $conf->{create_table};
if ( has_value($q->{$conf->{q_code}}) ) {
$self->process_q_code;
}
elsif ( has_value($q->{pulfp_ask_link}) ) {
$self->process_ask_link;
}
else {
$self->process_initial;
}
$t->{t}{plug_forgot_password} = $self->{OUTPUT};
}
sub process_q_code {
my $self = shift;
my $q = $self->{Q};
my $conf = $self->{CONF};
my $code = $q->{ $conf->{q_code} };
my $dbh = $self->dbh;
my $entry = ($dbh->selectall_arrayref(
'SELECT * FROM `' . $conf->{code_table}
. '` WHERE `code` = ?',
{ Slice => {} },
$code,
) || [])->[0];
unless ( $entry ) {
$self->set_stage('code_invalid');
$self->{OUTPUT} = '<p class="reset_code_expired">Your reset'
. ' code has expired. Please try'
. ' resetting your password again.</p>';
return;
}
if ( has_value( $q->{pulfp_has_change_pass} ) ) {
lib/App/ZofCMS/Plugin/UserLogin/ForgotPassword.pm view on Meta::CPAN
<form action="" method="POST" id="plug_forgot_password_form">
<div>
<p>Please enter your login into the form below and an email with
further instructions will be sent to you.</p>
<input type="hidden" name="page" value="<tmpl_var escape='html'
name='page'>">
<input type="hidden" name="pulfp_ask_link" value="1">
<tmpl_if name='error'>
<p class="error"><tmpl_var escape='html' name='error'></p>
</tmpl_if>
<label for="pulfp_login">Your login: </label
><input type="text"
class="input_text"
name="pulfp_login"
id="pulfp_login">
<tmpl_var name="submit_button">
</div>
</form>
END_FORM
}
1;
__END__
=encoding utf8
=head1 NAME
App::ZofCMS::Plugin::UserLogin::ForgotPassword - addon plugin that adds functionality to let users reset passwords
=head1 SYNOPSIS
In your L<HTML::Template> template:
<tmpl_var name='plug_forgot_password'>
In your Main Config File or ZofCMS Template:
plugins => [ qw/UserLogin::ForgotPassword/ ],
plug_user_login_forgot_password => {
# mandatory
dsn => "DBI:mysql:database=test;host=localhost",
# everything below is optional...
# ...arguments' default values are shown
user => '',
pass => undef,
opt => { RaiseError => 1, AutoCommit => 1 },
users_table => 'users',
code_table => 'users_forgot_password',
q_code => 'pulfp_code',
max_abuse => '5:10:60', # 5 min. intervals, max 10 attempts per 60 min.
min_pass => 6,
code_expiry => 24*60*60, # 1 day
code_length => 6,
subject => 'Password Reset',
email_link => undef, # this will be guessed
from => undef,
email_template => undef, # use plugin's default template
create_table => undef,
login_page => '/',
mime_lite_params => undef,
email => undef, # use `email` column in users table
button_send_link => q|<input type="submit" class="input_submit"|
. q| value="Send password">|,
button_change_pass => q|<input type="submit" class="input_submit"|
. q| value="Change password">|,
use_stage_indicators => 1,
no_run => undef,
},
=head1 DESCRIPTION
The module is a plugin for L<App::ZofCMS> that adds functionality to
L<App::ZofCMS::Plugin::UserLogin> plugin; that being the "forgot password?"
operations. Namely, this involves showing the user the form to ask for
their login, emailing the user special link which to follow (this is to
establish ligitimate reset) and, finally, to provide a form where a user
can enter their new password (and of course, the plugin will update
the password in the C<users> table). Wow, a mouthful of functionality! :)
This documentation assumes you've read L<App::ZofCMS>, L<App::ZofCMS::Config> and L<App::ZofCMS::Template>. Whilst not necessary,
being familiar with L<App::ZofCMS::Plugin::UserLogin> might be helpful.
=head1 GENERAL OUTLINE OF THE WAY PLUGIN WORKS
Here's the big picture of what the plugin does: user visits a page, plugin
shows the HTML form that asks the user to enter their login in order to
request password reset.
Once the user does that, the plugin checks that the provided login indeed
exists, checks that there's no abuse going on (flooding with reset
requests), generates a special "code" that, as part of a full
link-to-follow, is sent to the user inviting them to click it to proceed
with the reset.
Once the user clicks the link in their email (and thus ends up back on your
site), the plugin will invite them to enter (and reenter to confirm)
their new password. Once the plugin ensures the password looks good,
it will update user's password in the database.
All this can be enabled on your site with a few keystroke, thanks to this
plugin :)
=head1 FIRST-LEVEL ZofCMS TEMPLATE AND MAIN CONFIG FILE KEYS
=head2 C<plugins>
plugins => [
{ 'UserLogin::ForgotPassword' => 2000 },
],
B<Mandatory>. You need to include the plugin in the list of plugins
to execute.
=head2 C<plug_user_login_forgot_password>
plug_user_login_forgot_password => {
# mandatory
dsn => "DBI:mysql:database=test;host=localhost",
# everything below is optional...
# ...arguments' default values are shown
user => '',
pass => undef,
opt => { RaiseError => 1, AutoCommit => 1 },
users_table => 'users',
code_table => 'users_forgot_password',
q_code => 'pulfp_code',
max_abuse => '5:10:60', # 5 min. intervals, max 10 attempts per 60 min.
min_pass => 6,
code_expiry => 24*60*60, # 1 day
code_length => 6,
subject => 'Password Reset',
email_link => undef, # this will be guessed
from => undef,
email_template => undef, # use plugin's default template
create_table => undef,
login_page => '/',
mime_lite_params => undef,
email => undef, # use `email` column in users table
button_send_link => q|<input type="submit" class="input_submit"|
. q| value="Send password">|,
button_change_pass => q|<input type="submit" class="input_submit"|
. q| value="Change password">|,
use_stage_indicators => 1,
no_run => undef,
},
# or
plug_user_login_forgot_password => sub {
my ( $t, $q, $config ) = @_;
...
return $hashref_to_assign_to_plug_user_login_forgot_password_key;
},
B<Mandatory>. Takes either a hashref or a subref as a value.
If subref is specified, its return value will be assigned to
C<plug_user_login_forgot_password> key as if it was already there.
If sub returns an C<undef>, then plugin will stop further processing.
The C<@_> of the subref will contain C<$t>, C<$q>, and C<$config>
(in that order), where C<$t> is ZofCMS Tempalate hashref,
C<$q> is query parameters hashref, and C<$config> is the
L<App::ZofCMS::Config> object. The hashref has a whole ton of possible
keys/values that control plugin's behavior; luckily, virtually all of them
are optional with sensible defaults. Possible keys/values for the hashref
are as follows:
=head3 C<dsn>
plug_user_login_forgot_password => {
dsn => "DBI:mysql:database=test;host=localhost",
...
B<Mandatory>. The C<dsn> key will be passed to L<DBI>'s
C<connect_cached()> method, see documentation for L<DBI> and
C<DBD::your_database> for the correct syntax for this one.
The example above uses MySQL database called C<test> which is
located on C<localhost>.
B<Defaults to:> C<"DBI:mysql:database=test;host=localhost">, which is
rather useless, so make sure to set your own :)
=head3 C<user>
plug_user_login_forgot_password => {
user => '',
...
B<Optional>. Specifies the user name (login) for the database. This can be
an empty string if, for example, you are connecting using SQLite
driver. B<Defaults to:> C<''> (empty string)
=head3 C<pass>
plug_user_login_forgot_password => {
lib/App/ZofCMS/Plugin/UserLogin/ForgotPassword.pm view on Meta::CPAN
If set to C<undef>, abuse control will be disabled.
=head4 first time code number
plug_user_login_forgot_password => {
max_abuse => '5:10:60',
...
Unless set to C<undef>, the argument's value must be three numbers
separated by colons. The first number indicates, in minutes, the interval
of time that must pass after a password reset request until another request
can be sent I<using the same login> (there's no per-IP protection, or
anything like that). B<Default first number is> C<5>.
=head4 second time code number
The second number indicates the maximum number of reset attempts
(again, per-login) that can be done in C<third number> interval of time.
For example, if the second number is 10 and third is 60, a user can request
password reset 10 times in 60 minutes and no more.
B<Default second number> is C<10>.
=head4 third time code number
The third number indicates, in minutes, the time interval used by the
second number. B<Default third number is> C<60>.
=head3 C<min_pass>
plug_user_login_forgot_password => {
min_pass => 6,
...
B<Optional>. Takes a positive integer as a value. Specifies the minimum
length (number of characters) for the new password the user provides.
B<Defaults to:> C<6>
=head3 C<code_expiry>
plug_user_login_forgot_password => {
code_expiry => 24*60*60, # 1 day
...
B<Optional>. Takes, in seconds, the time after which to deem the
reset code (request) as expired. In other words, if the user requests
password reset, then ignores his email for C<code_expiry> seconds,
then the link in his email will no longer work, and he would have to
request the reset all over again. B<Defaults to:> C<86400> (24 hours)
=head3 C<code_length>
plug_user_login_forgot_password => {
code_length => 6,
...
B<Optional>. Specifies the length of the randomly generated code that
is used to identify legitimate user. Since this code is sent to the
user via email, and is directly visible, specifying the code to be
of too much length will look rather ugly. On the other hand, too short
of a code can be easily guessed by a vandal.
B<Defaults to:> C<6>
=head3 C<subject>
plug_user_login_forgot_password => {
subject => 'Password Reset',
...
B<Optional>. Takes a string as a value, this will be used as the subject
line of the email sent to the user (the one containing the link to click).
B<Defaults to:> C<Password Reset>
=head3 C<from>
plug_user_login_forgot_password => {
from => undef,
...
plug_user_login_forgot_password => {
from => 'Zoffix Znet <zoffix@cpan.org>',
...
B<Optional>. Takes a scalar as a value that specifies the C<From> field for
your email. If not specified, the plugin will simply not set the C<From>
argument in L<MIME::Lite>'s C<new()> method (which is what this plugin uses
under the hood). See L<MIME::Lite>'s docs for more description.
B<Defaults to:> C<undef> (not specified)
=head3 C<email_link>
plug_user_login_forgot_password => {
email_link => undef, # guess the right page
...
# note how the URI ends with the "invitation" to append the reset
# ... code right to the end
plug_user_login_forgot_password => {
email_link => 'http://foobar.com/your_page?foo=bar&pulfp_code=',
...
B<Optional>. Takes either C<undef> or a string containing a link
as a value. Specifies the link to the page with this plugin enabled, this
link will be emailed to the user so that they could proceed to
enter their new password. When set to C<undef>, the plugin guesses the
current page (using C<%ENV>) and that's what it will use for the link.
If you specify the string, make sure to end it with C<pulfp_code=> (note
the equals sign at the end), where C<pulfp_code> is the value you have set
for C<q_code> argument. B<Defaults to:> C<undef> (makes the plugin guess
the right link)
=head3 C<email_template>
plug_user_login_forgot_password => {
email_template => undef, # use plugin's default template
...
plug_user_login_forgot_password => {
email_template => \'templates/file.tmpl', # read template from file
...
plug_user_login_forgot_password => {
email_template => '<p>Blah blah blah...', # use this string as template
...
B<Optional>. Takes a scalar, a scalar ref, or C<undef> as a value.
Specifies L<HTML::Template> template to use when generating the email
with the reset link. When set to C<undef>, plugin will use its default
template (see OUTPUT section below). If you're using your own template,
the C<link> template variable will contain the link the user needs to follow
(i.e., use C<< <tmpl_var escape='html' name='link'> >>).
B<Defaults to:> C<undef> (plugin's default, see OUTPUT section below)
=head3 C<login_page>
plug_user_login_forgot_password => {
login_page => '/',
...
plug_user_login_forgot_password => {
login_page => '/my-login-page',
...
plug_user_login_forgot_password => {
login_page => 'http://lolwut.com/your-login-page',
...
B<Optional>. As a value, takes either C<undef> or a URI. Once the user is
through will all the stuff plugin wants them to do, the plugin will tell
them that the password has been changed, and that they can no go ahead
and "log in". If C<login_page> is specified, the "log in" text will be
a link pointing to whatever you set in C<login_page>; otherwise, the
"log in" text will be just plain text. B<Defaults to:> C</> (i.e. web root)
=head3 C<mime_lite_params>
plug_user_login_forgot_password => {
mime_lite_params => undef,
...
plug_user_login_forgot_password => {
mime_lite_params => [
'smtp',
'meowmail',
Auth => [ 'FOO/bar', 'p4ss' ],
],
...
B<Optional>. Takes an arrayref or C<undef> as a value.
( run in 0.421 second using v1.01-cache-2.11-cpan-39bf76dae61 )