Apache2-Controller

 view release on metacpan or  search on metacpan

lib/Apache2/Controller/Auth/OpenID.pm  view on Meta::CPAN

package Apache2::Controller::Auth::OpenID;

=head1 NAME

Apache2::Controller::Auth::OpenID - OpenID for Apache2::Controller

=head1 VERSION

Version 1.001.001 - THIS MODULE DISABLED, DOES NOT WORK.

=cut

use version;
our $VERSION = version->new('1.001.001');

=head1 SYNOPSIS

 PerlLoadModule Apache2::Controller::Directives

 <Location /myapp>
     SetHandler modperl

     # uri to your login controller:
     A2C_Auth_OpenID_Login          login

     # uri to your logout controller:
     A2C_Auth_OpenID_Logout         logout

     # uri to your registration controller:
     A2C_Auth_OpenID_Register       register
     # you might want to put this outside the protected area, 
     # i.e. /other/register - you can use leading '/' for absolute uri

     # idle timeout in seconds, +2m, +3h, +4D, +6M, +7Y, or 'no timeout'
     # default is 1 hour.  a month is actually 30 days, a year 365.
     A2C_Auth_OpenID_Timeout        +1h

     # name of the openid table in database:
     A2C_Auth_OpenID_Table          openid
     
     # key of the username field in table:
     A2C_Auth_OpenID_User_Field     uname

     # key of the openid url field in table:
     A2C_Auth_OpenID_URL_Field      openid_url

     # if you use multiple DBI handles, name the one in pnotes
     # that you should use for reading the openid table:
     A2C_Auth_OpenID_DBI_Name       dbh

     # by default trust_root is the result of $r->construct_url(''),
     # i.e. the top of the site (see Apache::URI)
     A2C_Auth_OpenID_Trust_Root     http://myapp.tld/somewhere

     # set a random string used as salt with time() to sha secret
     A2C_Auth_OpenID_Consumer_Secret

     # but that random salt will be reset if you restart server,
     # which may cause current logins to die, so you can specify
     # your own constant salt of arbitrary length
     A2C_Auth_OpenID_Consumer_Secret    abcdefg1234567

     # if you do not want to preserve GET/POST params 
     # across redirects to the OpenID server, use this flag:
     # A2C_Auth_OpenID_NoPreserveParams

     # if you do not overload get_uname() (see below), then
     # PerlHeaderParserHandlers must be invoked in order
     # to set up the dbi handle before checking auth
     # with the default method.  In this example,
     # MyApp::DBI::Connector is an Apache2::Controller::DBI::Connector
     # and MyApp::Session is an Apache2::Controller::Session::Cookie...
     # see those modules for more info.

     PerlInitHandler            MyApp::Dispatch 
     PerlHeaderParserHandler    MyApp::DBI::Connector
     PerlHeaderParserHandler    MyApp::Session
     PerlHeaderParserHandler    Apache2::Controller::Auth::OpenID
 </Location>

=head1 DESCRIPTION

Implements an authentication mechanism for L<Apache2::Controller>
that uses OpenID.  

This is NOT an AuthenPerlHandler.  This is an implementation
of a simple cookie-based mechanism that shows the browser
a login page, where your controller should present and process an
HTML form for logging in.  

If you want an authentication handler that uses browser-based auth
(the pop-up dialog implemented by HTTP auth protocol) use 
L<Apache::Authen::OpenID>, which is not a part of Apache2::Controller
but should work for you anyway.

Natively this depends on L<Apache2::Controller::Session::Cookie>

lib/Apache2/Controller/Auth/OpenID.pm  view on Meta::CPAN


Correct trailing double /'s etc. in the openid url.

=cut

sub openid_url_normalize {
    my ($self, $openid_url) = @_;
    my $orig_url = $openid_url;
    my ($scheme) = $openid_url =~ m{ \A (\w+) : // }mxs;
    $scheme ||= 'http';
    $scheme = lc $scheme;
    $openid_url = URI->new( $openid_url, $scheme )->canonical->as_string
        || a2cx "Could not normalize openid_url '$orig_url'";
    $openid_url =~ s{ /+ \z }{}mxs;
    return $openid_url;
}

=head2 process

Make sure the config directives are assigned or use defaults.

If uri = login uri, process accordingly.

If uri = logout uri, delete session hash login flags and return OK.

=cut

sub _save_errs_in_sess {
    my ($self, $openid_csr) = @_;
    my $sess = $self->pnotes->{a2c}{session};
    my $errcode = $openid_csr->errcode 
        || $openid_csr->{last_errcode} || '[ no error code ]';
    my $errtext = $openid_csr->err 
        || $openid_csr->{last_errtext} || '[ no error text ]';
    $sess->{a2c}{openid}{errtext} = $errtext;
    $sess->{a2c}{openid}{errcode} = $errcode;
    return ($errtext, $errcode);
}

my ($openid_csr, $consumer_secret_string);
my %params_hash;

sub process {
    my ($self) = @_;
    my $uri = $self->uri();

  # my $pnotes = $self->pnotes;
  # DEBUG sub { "Before checking session, pnotes is:\n".Dump($pnotes) };

    # make sure a session object is set up already
    my $sess = $self->pnotes->{a2c}{session}
        || a2cx "No session object configured for handler";

    DEBUG sub { "Entering, processing uri '$uri'.\nsession is:\n".Dump($sess) };

    my $directives = $self->get_directives();
    my %conf = (
        $self->default_directives(),
        ( map {(lc($_) => $directives->{"A2C_Auth_OpenID_$_"})} 
          grep exists $directives->{"A2C_Auth_OpenID_$_"}, qw( 
            Login       Logout      Register    Timeout
            Table       User_Field  URL_Field   DBI_Name    
            Trust_Root  LWP_Class   Allow_Login Consumer_Secret
            NoPreserveParams
        ) ),
    );

    # make a lookup verification map of the internal uris
    $conf{is_internal} = { map {($conf{$_} => 1)} qw( login logout register ) };

    # slap in anything specified in the sub-hash of LWP class options
    my $lwp_opts_directive = $directives->{A2C_Auth_OpenID_LWP_Opts} || { };
    my @lwp_opt_keys = keys %{$lwp_opts_directive};
    DEBUG sub { "Trying to slice in lwp_opts: ".Dump($lwp_opts_directive) };
    @{$conf{lwp_opts}}{@lwp_opt_keys} = @{$lwp_opts_directive}{@lwp_opt_keys}
        if scalar @lwp_opt_keys;

    $self->{conf} = \%conf;

    DEBUG sub { "conf:\n".Dump(\%conf) };
    DEBUG sub { "session:\n".Dump($self->pnotes->{a2c}{session}) };

    # if we're on the register page, allow it through
    return Apache2::Const::OK if $uri eq $self->qualify_uri($conf{register});

    # logout and return if we're processing the logout uri
    if ($uri eq $self->qualify_uri($conf{logout})) {
        DEBUG "requested logout page $conf{logout}, returning logout()";
        return $self->logout();
    }

    # return OK if their session is logged in and timestamp is current
    if ($self->is_logged_in) {
        DEBUG "user is logged in, returning OK";
        return Apache2::Const::OK;
    }
    else {
        DEBUG "user is NOT logged in, continuing auth";
    }

    # consumer object creation is very slow, so we cache in package space:
    if (!defined $openid_csr) {
        my $cache = $self->can('cache') ? $self->cache() : undef;
        eval "use $conf{lwp_class}";
        a2cx "Could not load A2C_Auth_OpenID_LWP_Class ($conf{lwp_class}): "
            ."$EVAL_ERROR" if $EVAL_ERROR;

        # stash string in package to avoid closure circle on this req's %conf.
        # we provide some hardcoded junk if they didn't use the directive 
        # to specify or generate some.
        $consumer_secret_string = $conf{consumer_secret};
        DEBUG "Setting up CSR with secret string '$consumer_secret_string'";

        $openid_csr = Net::OpenID::Consumer->new(
            ua              => $conf{lwp_class}->new(%{ $conf{lwp_opts} }),
            cache           => $cache,
            consumer_secret => sub {
                my ($time) = @_;
                return sha224_base64("$time-$consumer_secret_string");
            },
            debug           => \&DEBUG,



( run in 0.804 second using v1.01-cache-2.11-cpan-39bf76dae61 )