Dancer2-Plugin-Auth-Extensible
view release on metacpan or search on metacpan
lib/Dancer2/Plugin/Auth/Extensible.pm view on Meta::CPAN
code => sub {
my $app = shift;
if ( $weak_plugin->logged_in_user ) {
# User is already logged in so redirect elsewhere
# uncoverable condition false
$app->redirect(
_return_url($app) || $weak_plugin->user_home_page );
}
# Reset password code submitted?
my ($code) = $app->request->splat;
if ( $code
&& $weak_plugin->reset_password_handler
&& $weak_plugin->user_password( code => $code ) )
{
$app->request->parameters->set('password_code_valid' => 1),
}
no strict 'refs';
return &{ $weak_plugin->login_page_handler }($weak_plugin);
},
);
$app->add_route(
method => 'get',
regexp => qr!^$denied_page$!,
code => sub {
my $app = shift;
$app->response->status(403);
no strict 'refs';
return &{ $weak_plugin->permission_denied_page_handler }($weak_plugin);
},
);
}
if ( !$plugin->no_login_handler ) {
my $login_page = $plugin->login_page;
my $logout_page = $plugin->logout_page;
# Match optional reset code, but not "denied"
$app->add_route(
method => 'post',
regexp => qr!^$login_page/?([\w]{32})?$!,
code => \&_post_login_route,
);
for my $method (qw/get post/) {
$app->add_route(
method => $method,
regexp => qr!^$logout_page$!,
code => \&_logout_route,
);
}
}
if ( $plugin->login_without_redirect ) {
# Add a post route so we can catch transparent login.
# This is a little sucky but since no hooks are called before
# route dispatch then adding this wildcard route now does at
# least make sure it gets added before any routes that use this
# plugin's route decorators are added.
$plugin->app->add_route(
method => 'post',
regexp => qr/.*/,
code => sub {
my $app = shift;
my $request = $app->request;
# See if this is actually a POST login.
my $username = $request->body_parameters->get(
'__auth_extensible_username');
my $password = $request->body_parameters->get(
'__auth_extensible_password');
if ( defined $username && defined $password ) {
my $auth_realm = $request->body_parameters->get(
'__auth_extensible_realm');
# Remove the auth params since the forward we call later
# will cause dispatch to retry this route again if
# the original route was a post since dispatch starts
# again from the start of the route list and this
# wildcard route will get hit again causing a loop.
foreach (qw/username password realm/) {
$request->body_parameters->remove(
"__auth_extensible_$_");
}
# Stash method and params since we delete these from
# the session if login is successful but we still need
# them for the forward to the original route after
# success.
my $method =
$app->session->read('__auth_extensible_method');
my $params =
$app->session->read('__auth_extensible_params');
# Attempt authentication.
my ( $success, $realm ) =
$weak_plugin->authenticate_user( $username,
$password, $auth_realm );
if ($success) {
$app->session->delete('__auth_extensible_params');
$app->session->delete('__auth_extensible_method');
# Change session ID if we have a new enough D2
# version with support.
$app->change_session_id
if $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" );
lib/Dancer2/Plugin/Auth/Extensible.pm view on Meta::CPAN
$plugin->execute_plugin_hook( 'permission_denied', $coderef );
# TODO: see if any code executed by that hook set up a response
$plugin->app->response->status(403);
my $options;
my $view = $plugin->denied_page;
my $template_engine = $plugin->app->template_engine;
my $path = $template_engine->view_pathname($view);
if ( !$template_engine->pathname_exists($path) ) {
$plugin->app->log(
debug => "app has no denied_page template defined" );
$options->{content} = $plugin->_render_template('login_denied.tt');
undef $view;
}
return $plugin->app->template( $view, undef, $options );
};
}
sub _check_for_login {
my ( $plugin, $coderef ) = @_;
$plugin->execute_plugin_hook( 'login_required', $coderef );
# TODO: see if any code executed by that hook set up a response
my $request = $plugin->app->request;
if ( $plugin->login_without_redirect ) {
my $tokens = {
login_failed => $request->var('login_failed'),
reset_password_handler => $plugin->reset_password_handler
};
# The WWW-Authenticate header added varies depending on whether
# the client is a robot or not.
my $ua = HTTP::BrowserDetect->new( $request->env->{HTTP_USER_AGENT} );
my $base = $request->base;
my $auth_method;
if ( !$ua->browser_string || $ua->robot ) {
$auth_method = $auth_method = qq{Basic realm="$base"};
}
else {
$auth_method = qq{FormBasedLogin realm="$base", }
. q{comment="use form to log in"};
}
$plugin->app->response->status(401);
$plugin->app->response->push_header(
'WWW-Authenticate' => $auth_method );
# If this is the first attempt to reach a protected page and *not*
# a failed passthrough login then we need to stash method and params.
if ( !$request->var('login_failed') ) {
$plugin->app->session->write(
'__auth_extensible_method' => lc($request->method) );
$plugin->app->session->write(
'__auth_extensible_params' => \%{ $request->params } );
}
return $plugin->_render_login_page( 'transparent_login.tt', $tokens );
}
# old-fashioned redirect to login page with return_url set
my $forward = $request->path;
$forward .= "?".$request->query_string
if $request->query_string;
return $plugin->app->redirect(
$request->uri_for(
# Do not use request_uri, as it is the raw string sent by the
# browser, not taking into account the application mount point.
# This means that when it is then concatenated with the base URL,
# the application mount point is specified twice. See GH PR #81
$plugin->login_page, { return_url => $forward }
)
);
}
sub _render_login_page {
my ( $plugin, $default_template, $tokens ) = @_;
# If app has its own login page view then use it
# otherwise render our internal one and pass that to 'template'.
my ( $view, $options ) = ( $plugin->login_template, {} );
my $template_engine = $plugin->app->template_engine;
my $path = $template_engine->view_pathname($view);
if ( !$template_engine->pathname_exists($path) ) {
$plugin->app->log( debug => "app has no login template defined" );
$options->{content} =
$plugin->_render_template( $default_template, $tokens );
undef $view;
}
return $plugin->app->template( $view, $tokens, $options );
}
sub _default_email_password_reset {
my ( $plugin, %options ) = @_;
my %message;
if ( my $password_reset_text = $plugin->password_reset_text ) {
no strict 'refs';
%message = &{$password_reset_text}( $plugin, %options );
}
else {
my $site = $plugin->app->request->uri_base;
my $appname = $plugin->app->config->{appname} || '[unknown]';
$message{subject} = "Password reset request";
$message{from} = $plugin->mail_from;
$message{plain} = <<__EMAIL;
A request has been received to reset your password for $appname. If
you would like to do so, please follow the link below:
$site/login/$options{code}
__EMAIL
}
$plugin->_send_email( to => $options{email}, %message );
}
sub _render_template {
my ( $plugin, $view, $tokens ) = @_;
( run in 1.363 second using v1.01-cache-2.11-cpan-39bf76dae61 )