use warnings;
use strict;
=head1 NAME
Jifty::Plugin::Authentication::Password::Action::Login - process login with password
=cut
package Jifty::Plugin::Authentication::Password::Action::Login;
use base qw/Jifty::Action/;
use Digest::MD5 qw(md5_hex);
use HTTP::Date ();
use constant TOKEN_EXPIRE_TIME => 30;
__PACKAGE__->mk_accessors( 'login_by' );
=head2 new
=cut
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
my $plugin =
Jifty->find_plugin('Jifty::Plugin::Authentication::Password');
$self->login_by( $plugin->{login_by} || 'email' );
return $self;
}
=head2 arguments
Return the email and password form fields
=cut
sub arguments {
my $self = shift;
my $login_para;
if ( $self->login_by eq 'username' ) {
$login_para = {
username => {
label => _( 'Username' ),
mandatory => 1,
ajax_validates => 1,
}
};
}
else {
$login_para = {
email => {
label => _( 'Email' ),
mandatory => 1,
ajax_validates => 1,
}
};
}
return( {
%$login_para,
password => { type => 'password',
label => _('Password'),
# mandatory in some cases; see validate_password
mandatory => 0,
},
hashed_password => { type => 'hidden',
label => _('Hashed Password'),
},
remember => { type => 'checkbox',
label => _('Remember me?'),
hints => _('Your browser can remember your login for you'),
default => 0,
},
token => { type => 'hidden',
label => 'token',
mandatory => 0 },
});
}
=head2 validate_email ADDRESS
Makes sure that the email submitted is a legal email and that there's a user in the database with it.
Overridden from Jifty::Action::Record.
=cut
sub validate_email {
my $self = shift;
my $email = shift;
return $self->_validate_email_or_username(email => $email);
}
=head2 validate_username ADDRESS
there's a user in the database with it.
Overridden from Jifty::Action::Record.
=cut
sub validate_username {
my $self = shift;
my $username = shift;
return $self->_validate_email_or_username(username => $username);
}
sub _validate_email_or_username {
my $self = shift;
my $name = shift;
my $value = shift;
if ($value) {
my $u = $self->load_user( $value);
return $self->validation_error(
$name => _("It doesn't look like there's an account by that name.")
) unless ( $u->id );
return $self->validation_ok($name);
}
return;
}
=head2 validate_password PASSWORD
Makes sure that the password submitted actually exists, unless there's a token and a hashed
password.
Overridden from Jifty::Action::Record.
=cut
sub validate_password {
my $self = shift;
my $pw = shift;
my $token = $self->argument_value('token') ||'';
my $hashedpw = $self->argument_value('hashed_password') ;
if ($token eq '') { # we have no token, validate in a standard way
unless ( defined $pw && length $pw ) {
return $self->validation_error(password => "You need to fill in the 'password' field" );
}
} else { # we have a token, so we should have a hashed pw
my $emptypw = '';
my $blankhash = md5_hex("$token $emptypw");
if ($hashedpw eq $blankhash) {
return $self->validation_error(password => "You need to fill in the 'password' field" );
}
}
return $self->validation_ok('password');
}
=head2 validate_token TOKEN
Make sure we issued the token within the last 30 seconds, otherwise
time out the request.
=cut
sub validate_token {
my $self = shift;
my $value = shift;
my $token = Jifty->web->session->get('login_token');
Jifty->web->session->remove('login_token');
if ($value) {
if(int $value < (time - TOKEN_EXPIRE_TIME)) {
return $self->validation_error(token => "Your login attempt has timed out. Please try again.");
}
if ($value ne $token) {
return $self->validation_error(token => "That didn't work. Please try again.");
}
}
return $self->validation_ok("token");
}
=head2 take_action
Actually check the user's password. If it's right, log them in.
Otherwise, throw an error.
=cut
sub take_action {
my $self = shift;
my $user = $self->load_user( $self->argument_value( $self->login_by ) );
my $password = $self->argument_value('password');
my $token = $self->argument_value('token') || '';
my $hashedpw = $self->argument_value('hashed_password');
my $BAD_PW = _('You may have mistyped your email or password. Give it another shot.');
if ( $token ne '' ) { # browser supports javascript, do password hashing
unless ( $user->id && $user->hashed_password_is( $hashedpw, $token ) )
{
$self->result->error($BAD_PW);
return;
}
} else { # no password hashing over the wire
unless ( $user->id && $user->password_is($password) ) {
$self->result->error($BAD_PW);
return;
}
}
unless ($user->email_confirmed) {
$self->result->error( _(q{You haven't confirmed your account yet.}) ); return;
}
# Set up our login message
$self->result->message( $self->login_message($user));
$self->result->content( id => $user->id );
# Actually do the signin thing.
Jifty->web->current_user(Jifty->app_class('CurrentUser')->new( id => $user->id));
Jifty->web->session->expires($self->argument_value('remember') ? HTTP::Date::time2str( time() + 31536000 ) : undef);
Jifty->web->session->set_cookie;
return 1;
}
=head2 load_user
Load up and return a YourApp::User object for the user trying to log in
=cut
sub load_user {
my $self = shift;
my $value = shift;
my $user = Jifty->app_class('Model', 'User')->new(current_user => Jifty->app_class('CurrentUser')->superuser);
# normally we use name as column name instead of usernmae
my $column = $self->login_by eq 'username' ? 'name' : $self->login_by;
$user->load_by_cols( $column => $value );
return $user;
}
=head2 login_message $user_object
Returns the "hi, you're logged in message"
=cut
sub login_message {
my $self = shift;
my $user = shift;
return _("Welcome back, %1.", $user->name);
}
1;