Dancer2-Plugin-Auth-Extensible
view release on metacpan or search on metacpan
lib/Dancer2/Plugin/Auth/Extensible.pm view on Meta::CPAN
if ( $module eq 'Mail::Message' ) {
# require Mail::Message;
require Mail::Message::Body::String;
return $plugin->_email_mail_message(@_);
}
else {
croak "No support for $module. Please submit a PR!";
}
}
sub _return_url {
my $app = shift;
my $return_url = $app->request->query_parameters->get('return_url')
|| $app->request->body_parameters->get('return_url')
or return undef;
$return_url = uri_unescape($return_url);
my $uri = URI->new($return_url);
# Construct a URL using uri_for, which ensures that the correct base domain
# is used (preventing open URL redirection attacks). The query needs to be
# parsed and passed as an option, otherwise it is not encoded properly
return $app->request->uri_for($uri->path, $uri->query_form_hash);
}
#
# routes
#
# implementation of logout route
sub _logout_route {
my $app = shift;
my $req = $app->request;
my $plugin = $app->with_plugin('Auth::Extensible');
$plugin->execute_plugin_hook( 'before_logout' );
$app->destroy_session;
if ( my $url = _return_url($app) ) {
$app->redirect( $url );
}
elsif ($plugin->exit_page) {
$app->redirect($plugin->exit_page);
}
else {
# TODO: perhaps make this more configurable, perhaps by attempting to
# render a template first.
return "OK, logged out successfully.";
}
}
# implementation of post login route
sub _post_login_route {
my $app = shift;
my $plugin = $app->with_plugin('Auth::Extensible');
my $params = $app->request->body_parameters->as_hashref;
# First check for password reset request, if applicable
if ( $plugin->reset_password_handler && $params->{submit_reset} ) {
my $username = $params->{username_reset};
croak "Attempt to pass reference to reset blocked" if ref $username;
$plugin->password_reset_send( username => $username );
return $app->forward(
$plugin->login_page,
{ reset_sent => 1 },
{ method => 'GET' }
);
}
# Then for a password reset itself (confirmed by POST request)
my ($code) =
$plugin->reset_password_handler
&& $params->{confirm_reset}
&& $app->request->splat;
if ($code) {
no strict 'refs';
my $randompw = &{ $plugin->password_generator };
if (my $username = $plugin->user_password( code => $code, new_password => $randompw ) ) {
# Support a custom 'Change password' page or other app-based
# intervention after a successful reset code has been applied
foreach my $realm_check (@{ $plugin->realm_names }) { # $params->{realm} isn't defined at this point...
my $provider = $plugin->auth_provider($realm_check);
$params->{realm} = $realm_check if $provider->get_user_details($username);
}
$plugin->execute_plugin_hook( 'after_reset_code_success',
{ username => $username, password => $randompw, realm => $params->{realm} } );
return $app->forward(
$plugin->login_page,
{ new_password => $randompw },
{ method => 'GET' }
);
}
}
# For security, ensure the username and password are straight scalars; if
# the app is using a serializer and we were sent a blob of JSON, they could
# have come from that JSON, and thus could be hashrefs (JSON SQL injection)
# - for database providers, feeding a carefully crafted hashref to the SQL
# builder could result in different SQL to what we'd expect.
# For instance, if we pass password => params->{password} to an SQL builder,
# we'd expect the query to include e.g. "WHERE password = '...'" (likely
# with paremeterisation) - but if params->{password} was something
# different, e.g. { 'like' => '%' }, we might end up with some SQL like
# WHERE password LIKE '%' instead - which would not be a Good Thing.
my $username = $params->{username} || $params->{__auth_extensible_username};
my $password = $params->{password} || $params->{__auth_extensible_password};
for ( $username, $password ) {
if ( ref $_ ) {
# TODO: handle more cleanly
croak "Attempt to pass a reference as username/password blocked";
}
}
if ( $plugin->logged_in_user ) {
# uncoverable condition false
$app->redirect( _return_url($app) || $plugin->user_home_page );
}
my $auth_realm = $params->{realm} || $params->{__auth_extensible_realm};
my ( $success, $realm ) =
$plugin->authenticate_user( $username, $password, $auth_realm );
if ($success) {
# change session ID if we have a new enough D2 version with support
$plugin->app->change_session_id
if $plugin->app->can('change_session_id');
$app->session->write( logged_in_user => $username );
$app->session->write( logged_in_user_realm => $realm );
$app->log( core => "Realm is $realm" );
$plugin->execute_plugin_hook( 'after_login_success' );
# uncoverable condition false
$app->redirect( _return_url($app) || $plugin->user_home_page );
}
else {
$app->request->vars->{login_failed}++;
$app->forward(
$plugin->login_page,
{ login_failed => 1 },
{ method => 'GET' }
);
}
}
#
# private functions
#
sub _default_password_generator {
Session::Token->new( length => 8 )->get;
}
sub _reset_code {
Session::Token->new( length => 32 )->get;
}
# Replacement for much maligned and misunderstood smartmatch operator
sub _smart_match {
my ( $got, $want ) = @_;
if ( !ref $want ) {
return $got eq $want;
}
elsif ( ref $want eq 'Regexp' ) {
return $got =~ $want;
}
elsif ( ref $want eq 'ARRAY' ) {
return grep { $_ eq $got } @$want;
}
else {
( run in 2.129 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )