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 )