view release on metacpan or search on metacpan
CGI::Application::Plugin::ActionDispatch.
Adding tests around the HTML generated by the login_box function and
verifying that it is taint free.
Upped requiredment on CGI to 3.16 as that seems to cause unusual behaviour in returning
self referring urls and further upped requirement on CGI to 3.16 as 3.15 has an
unusual interpretation of redirection headers.
Fixed regular expressions in destination test to respect HTTP header CRLF conventions.
0.17 Thu Jan 21 19:16 GMT 2010
Bug Fixes
rt53533 - During initialization deferred checking the users credentials to the last possible moment.
This ensures that sessions are not created unless actually required.
Also attempted to document which functions add session/cookie state.
Build Fixes
rt35030 - Upped the requirement on Apache::Htpasswd to 1.8 as this simplifies our dependency management.
0.16 Mon Jan 18 22:18 GMT 2010
Release dedicated to improving CPAN readiness - see RT:50670.
Fixed spelling mistakes and added a test script that uses Test::Spelling.
Fixed dependency on Test::Exception and added a test script that uses Test::Prereq::Build.
API Changes
- renamed the 'auth' method to 'authen' to make it easier
to distinguish from the upcoming 'authz' plugin. There
is also an alias to 'authentication' for those who like
clarity over brevity.
- For the DBI driver, changed credential identifiers from
cred_1 to __CREDENTIAL_1__
New Drivers
- Dummy Driver - accepts any credentials as valid
New Stuff
- Added little icons to the default login and password
fields (only works in Mozilla based browsers)
- if STORE config is not given, the plugin will detect if
you are already using the Session plugin, and use the
Session Store instead of the Cookie Store.
- Added a lot of new documentation
Bug Fixes
CGI::Application::Plugin::Authentication adds the ability to
authenticate users in your CGI::Application modules. It imports one
method called 'authen' into your CGI::Application module. Through the
authen method you can call all the methods of the
CGI::Application::Plugin::Authentication plugin.
There are two main decisions that you need to make when using this
module. How will the usernames and password be verified (i.e. from a
database, LDAP, etc...), and how can we keep the knowledge that a user
has already logged in persistent, so that they will not have to enter
their credentials again on the next request (i.e. how do we 'Store' the
authentication information across requests).
Choosing a Driver
There are three drivers that are included with the distribution. Also,
there is built in support for all of the Authen::Simple modules (search
CPAN for Authen::Simple for more information). This should be enough to
cover everyone's needs.
If you need to authenticate against a source that is not provided, you
can use the Generic driver which will accept either a hash of
username/password pairs, or an array of arrays of credentials, or a
subroutine reference that can verify the credentials. So through the
Generic driver you should be able to write your own verification system.
There is also a Dummy driver, which blindly accepts any credentials
(useful for testing). See the
CGI::Application::Plugin::Authentication::Driver::Generic,
CGI::Application::Plugin::Authentication::Driver::DBI and,
CGI::Application::Plugin::Authentication::Driver::Dummy docs for more
information on how to use these drivers. And see the Authen::Simple
suite of modules for information on those drivers.
Choosing a Store
The Store modules keep information about the authentication status of
the user persistent across multiple requests. The information that is
own login page, I would recommend that you start with the HTML code for
the default login page, so that your login page will contain the correct
form fields and hidden fields.
Ticket based authentication
This Authentication plugin can handle ticket based authentication
systems as well. All that is required of you is to write a Store module
that can understand the contents of the ticket. The Authentication
plugin will require at least the 'username' to be retrieved from the
ticket. A Ticket based authentication scheme will not need a Driver
module at all, since the actual verification of credentials is done by
an external authentication system, possibly even on a different host.
You will need to specify the location of the login page using the
LOGIN_URL configuration variable, and unauthenticated users will
automatically be redirected to your ticket authentication login page.
EXPORTED METHODS
authen
This is the only method exported from this module. Everything is
controlled through this method call, which will return a
CGI::Application::Plugin::Authentication object, or just the class name
The following parameters are accepted:
DRIVER
Here you can choose which authentication module(s) you want to use
to perform the authentication. For simplicity, you can leave off the
CGI::Application::Plugin::Authentication::Driver:: part when
specifying the DRIVER name If this module requires extra parameters,
you can pass an array reference that contains as the first parameter
the name of the module, and the rest of the values in the array will
be considered options for the driver. You can provide multiple
drivers which will be used, in order, to check the credentials until
a valid response is received.
DRIVER => 'Dummy' # let anyone in regardless of the password
- or -
DRIVER => [ 'DBI',
DBH => $self->dbh,
TABLE => 'user',
CONSTRAINTS => {
created already.
is_new_login
This will return true or false depending on if this is a fresh login
$self->log->info("New Login") if $self->authen->is_new_login;
This function will initiate a session or cookie if one has not been
created already.
credentials
This method will return the names of the form parameters that will be
looked for during a login. By default they are authen_username and
authen_password, but these values can be changed by supplying the
CREDENTIALS parameters in the configuration. Calling this function, will
not itself generate cookies or session ids.
logout
This will attempt to logout the user. If during a request the
Authentication module sees a parameter called 'authen_logout', it will
automatically call this method to log out the user.
$self->authen->logout();
This function will initiate a session or cookie if one has not been
created already.
drivers
This method will return a list of driver objects that are used for
verifying the login credentials. Calling this function, will not itself
generate cookies or session ids.
store
This method will return a store object that is used to store information
about the status of the authentication across multiple requests. This
function will initiate a session or cookie if one has not been created
already.
initialize
This does most of the heavy lifting for the Authentication plugin. It
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
=head1 DESCRIPTION
CGI::Application::Plugin::Authentication adds the ability to authenticate users
in your L<CGI::Application> modules. It imports one method called 'authen' into your
CGI::Application module. Through the authen method you can call all the methods of
the CGI::Application::Plugin::Authentication plugin.
There are two main decisions that you need to make when using this module. How will
the usernames and password be verified (i.e. from a database, LDAP, etc...), and how
can we keep the knowledge that a user has already logged in persistent, so that they
will not have to enter their credentials again on the next request (i.e. how do we 'Store'
the authentication information across requests).
=head2 Choosing a Driver
There are three drivers that are included with the distribution. Also, there
is built in support for all of the Authen::Simple modules (search CPAN for
Authen::Simple for more information). This should be enough to cover
everyone's needs.
If you need to authenticate against a source that is not provided, you can use
the Generic driver which will accept either a hash of username/password pairs,
or an array of arrays of credentials, or a subroutine reference that can verify
the credentials. So through the Generic driver you should be able to write
your own verification system. There is also a Dummy driver, which blindly
accepts any credentials (useful for testing). See the
L<CGI::Application::Plugin::Authentication::Driver::Generic>,
L<CGI::Application::Plugin::Authentication::Driver::DBI> and,
L<CGI::Application::Plugin::Authentication::Driver::Dummy> docs for more
information on how to use these drivers. And see the L<Authen::Simple> suite
of modules for information on those drivers.
=head2 Choosing a Store
The Store modules keep information about the authentication status of the user persistent
across multiple requests. The information that is stored in the store include the username,
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
If you plan to create your own login page, I would recommend that you start with the HTML
code for the default login page, so that your login page will contain the correct form
fields and hidden fields.
=head2 Ticket based authentication
This Authentication plugin can handle ticket based authentication systems as well. All that
is required of you is to write a Store module that can understand the contents of the ticket.
The Authentication plugin will require at least the 'username' to be retrieved from the
ticket. A Ticket based authentication scheme will not need a Driver module at all, since the
actual verification of credentials is done by an external authentication system, possibly
even on a different host. You will need to specify the location of the login page using
the LOGIN_URL configuration variable, and unauthenticated users will automatically
be redirected to your ticket authentication login page.
=head1 EXPORTED METHODS
=head2 authen
This is the only method exported from this module. Everything is controlled
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
=over 4
=item DRIVER
Here you can choose which authentication module(s) you want to use to perform the authentication.
For simplicity, you can leave off the CGI::Application::Plugin::Authentication::Driver:: part
when specifying the DRIVER name If this module requires extra parameters, you
can pass an array reference that contains as the first parameter the name of the module,
and the rest of the values in the array will be considered options for the driver. You can provide
multiple drivers which will be used, in order, to check the credentials until
a valid response is received.
DRIVER => 'Dummy' # let anyone in regardless of the password
- or -
DRIVER => [ 'DBI',
DBH => $self->dbh,
TABLE => 'user',
CONSTRAINTS => {
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
croak "authen config error: parameter LOGOUT_URL is not a string"
if ref $props->{LOGOUT_URL};
$config->{LOGOUT_URL} = delete $props->{LOGOUT_URL};
}
# Check for CREDENTIALS
if ( defined $props->{CREDENTIALS} ) {
croak "authen config error: parameter CREDENTIALS is not a string or arrayref"
if ref $props->{CREDENTIALS} && Scalar::Util::reftype( $props->{CREDENTIALS} ) ne 'ARRAY';
$config->{CREDENTIALS} = delete $props->{CREDENTIALS};
# We will accept a string, but what we really want is an arrayref of the credentials
no warnings qw(uninitialized);
$config->{CREDENTIALS} = [ $config->{CREDENTIALS} ] if Scalar::Util::reftype( $config->{CREDENTIALS} ) ne 'ARRAY';
}
# Check for LOGIN_SESSION_TIMEOUT
if ( defined $props->{LOGIN_SESSION_TIMEOUT} ) {
croak "authen config error: parameter LOGIN_SESSION_TIMEOUT is not a string or a hashref"
if ref $props->{LOGIN_SESSION_TIMEOUT} && ref$props->{LOGIN_SESSION_TIMEOUT} ne 'HASH';
my $options = {};
if (! ref $props->{LOGIN_SESSION_TIMEOUT}) {
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
=cut
sub is_new_login {
my $self = shift;
$self->initialize;
return $self->{is_new_login};
}
=head2 credentials
This method will return the names of the form parameters that will be
looked for during a login. By default they are authen_username and authen_password,
but these values can be changed by supplying the CREDENTIALS parameters in the
configuration. Calling this function, will not itself generate cookies or session ids.
=cut
sub credentials {
my $self = shift;
my $config = $self->_config;
return $config->{CREDENTIALS} || [qw(authen_username authen_password)];
}
=head2 logout
This will attempt to logout the user. If during a request the Authentication
module sees a parameter called 'authen_logout', it will automatically call this method
to log out the user.
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
sub logout {
my $self = shift;
$self->initialize;
$self->store->clear;
}
=head2 drivers
This method will return a list of driver objects that are used for
verifying the login credentials. Calling this function, will not itself generate cookies or session ids.
=cut
sub drivers {
my $self = shift;
if ( !$self->{drivers} ) {
my $config = $self->_config;
# Fetch the configuration parameters for the driver(s)
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
}
my $config = $self->_config;
# See if the user is trying to log in
# We do this before checking to see if the user is already logged in, since
# a logged in user may want to log in as a different user.
my $field_names = $config->{CREDENTIALS} || [qw(authen_username authen_password)];
my $query = $self->_cgiapp->query;
my @credentials = map { scalar $query->param($_) } @$field_names;
if ($credentials[0]) {
# The user is trying to login
# make sure if they are already logged in, that we log them out first
my $store = $self->store;
$store->clear if $store->fetch('username');
foreach my $driver ($self->drivers) {
if (my $username = $driver->verify_credentials(@credentials)) {
# This user provided the correct credentials
# so save this new login in the store
my $now = time();
$store->save( username => $username, login_attempts => 0, last_login => $now, last_access => $now );
$self->{is_new_login} = 1;
# See if we are remembering the username for this user
my $login_config = $config->{LOGIN_FORM} || {};
if ($login_config->{REMEMBERUSER_OPTION} && scalar $query->param('authen_rememberuser')) {
my $cookie = $query->cookie(
-name => $login_config->{REMEMBERUSER_COOKIENAME} || 'CAPAUTHTOKEN',
-value => $username,
lib/CGI/Application/Plugin/Authentication.pm view on Meta::CPAN
This runmode is provided if you do not want to create your
own login runmode. It will display a simple login form for the user, which
can be replaced by assigning RENDER_LOGIN a coderef that returns the HTML.
=cut
sub authen_login_runmode {
my $self = shift;
my $authen = $self->authen;
my $credentials = $authen->credentials;
my $username = $credentials->[0];
my $password = $credentials->[1];
my $html;
if ( my $sub = $authen->_config->{RENDER_LOGIN} ) {
$html = $sub->($self);
}
else {
$html = join( "\n",
CGI::start_html( -title => $authen->display->login_title ),
$authen->display->login_box,
CGI::end_html(),
lib/CGI/Application/Plugin/Authentication/Display/Basic.pm view on Meta::CPAN
my $class = shift;
my $cgiapp = shift;
my $self = CGI::Application::Plugin::Authentication::Display->new($cgiapp);
bless $self, $class;
return $self;
}
sub login_box {
my $self = shift;
croak "already authenticated" if $self->_cgiapp->authen->is_authenticated;
my $credentials = $self->_cgiapp->authen->credentials;
my $runmode = $self->_cgiapp->get_current_runmode;
my $destination = $self->_cgiapp->authen->_detaint_destination || $self->_cgiapp->authen->_detaint_selfurl;
my $action = $self->_cgiapp->authen->_detaint_url;
my $username = $credentials->[0];
my $password = $credentials->[1];
my $login_form = $self->_cgiapp->authen->_config->{LOGIN_FORM};
my %options = (
TITLE => 'Sign In',
USERNAME_LABEL => 'User Name',
PASSWORD_LABEL => 'Password',
SUBMIT_LABEL => 'Sign In',
COMMENT => 'Please enter your username and password in the fields below.',
REMEMBERUSER_OPTION => 1,
REMEMBERUSER_LABEL => 'Remember User Name',
REMEMBERUSER_COOKIENAME => 'CAPAUTHTOKEN',
lib/CGI/Application/Plugin/Authentication/Display/Classic.pm view on Meta::CPAN
sub new {
my $class = shift;
my $cgiapp = shift;
my $self = CGI::Application::Plugin::Authentication::Display->new($cgiapp);
bless $self, $class;
return $self;
}
sub login_box {
my $self = shift;
my $credentials = $self->_cgiapp->authen->credentials;
my $runmode = $self->_cgiapp->get_current_runmode;
my $destination = $self->_cgiapp->authen->_detaint_destination || $self->_cgiapp->authen->_detaint_selfurl;
my $action = $self->_cgiapp->authen->_detaint_url;
my $username = $credentials->[0];
my $password = $credentials->[1];
my $login_form = $self->_cgiapp->authen->_config->{LOGIN_FORM} || {};
my %options = (
TITLE => 'Sign In',
USERNAME_LABEL => 'User Name',
PASSWORD_LABEL => 'Password',
SUBMIT_LABEL => 'Sign In',
COMMENT => 'Please enter your username and password in the fields below.',
REMEMBERUSER_OPTION => 1,
REMEMBERUSER_LABEL => 'Remember User Name',
REMEMBERUSER_COOKIENAME => 'CAPAUTHTOKEN',
lib/CGI/Application/Plugin/Authentication/Driver.pm view on Meta::CPAN
=head1 NAME
CGI::Application::Plugin::Authentication::Driver - Base module for building driver classes
for CGI::Application::Plugin::Authentication
=head1 SYNOPSIS
package CGI::Application::Plugin::Authentication::Driver::MyDriver;
use base qw(CGI::Application::Plugin::Authentication::Driver);
sub verify_credentials {
my $self = shift;
my @credentials = @_;
if ( >>> Validate Credentials <<< ) {
return $credentials[0];
}
return;
}
=head1 DESCRIPTION
This module is a base class for all driver classes for the L<CGI::Application::Plugin::Authentication>
plugin. Each driver class is required to provide only one method to validate the given credentials.
Normally only two credentials will be passed in (username and password), but you can configure the plugin
to handle any number of credentials (for example you may require the user to enter a group name, or domain name
as well as a username and password).
=head1 FIELD FILTERS
It is quite common for passwords to be stored using some form of one way encryption. Unix crypt being the
old standard in the Unix community, however MD5 or SHA1 hashes are more popular today. In order to
simplify the validation routines some methods have been provided to help test these passwords. When
configuring a Driver (and if the driver supports it), you can specify which fields are encoded, and which
method is used for the encoding by specifying a filter on the field in question.
lib/CGI/Application/Plugin/Authentication/Driver.pm view on Meta::CPAN
if ($marker) {
return $option;
} elsif ($option eq $key) {
# We need the next element
$marker = 1;
}
}
return;
}
=head2 verify_credentials
This method needs to be provided by the driver class. It needs to be an object method that accepts a list of
credentials, and will verify that the credentials are valid, and return a username that will be used to identify
this login (usually you will just return the value of the first credential, however you are not bound to that)..
=cut
sub verify_credentials {
die "verify_credentials must be implemented in the subclass";
}
=head2 filter
This method can be used to filter a field (usually password fields) using a number of standard or
custom encoding techniques. See the section on Builtin Filters above to see what filters are available
When using a custom filter, you will need to provide a FILTERS option in the configuration of the DRIVER (See the
section on FIELD FILTERS above for an example). By default, if no filter is specified, it is
returned as is. This means that you can run all fields through this function even if they
don't have any filters to simplify the driver code.
lib/CGI/Application/Plugin/Authentication/Driver/Authen/Simple.pm view on Meta::CPAN
=head1 EXAMPLE
__PACKAGE__->authen->config(
DRIVER => [ 'Authen::Simple::CDBI', class => 'MyApp::Model::User' ],
);
=head1 METHODS
=head2 verify_credentials
This method will test the provided credentials against the Authen::Simple module
that was configured.
=cut
sub verify_credentials {
my $self = shift;
my @creds = @_;
my @options = $self->options;
my $authen_class = shift @options;
return undef unless defined $creds[0] && defined $creds[1];
$authen_class->require || Carp::croak("The $authen_class module is not installed");
my $authen_obj = $authen_class->new(@options);
lib/CGI/Application/Plugin/Authentication/Driver/DBI.pm view on Meta::CPAN
JOIN_ON => 'user.domainid = domain.id',
- or -
JOIN_ON => 'U.domainid = D.id',
=head2 COLUMNS (optional)
This is a hash of columns/values that should be pulled out of the database and validated
locally in perl. Most credentials can be checked right in the database (example
username = ?), but some parameters may need to be tested locally in perl, so they
must be listed in the COLUMNS option. One example of a value that needs to be tested
in perl is a crypted password. In order to test a crypted password, you need to
take the entered password, and crypt it with the salt of the already crypted password.
But until we actually see the password that is in the database, we will not know the
value of the salt that was used to encrypt the password. So we pull the value out
using COLUMNS, and the test will be performed automatically in perl.
Any value that matches __CREDENTIAL_n__ (where n is a number) will be replaced with
the corresponding credential that was entered by the user. For an explanation of
what the credentials are and where they come from, see the section headed with
CREDENTIALS in L<CGI::Application::Plugin::Authentication>.
COLUMNS => { 'crypt:password' => '__CREDENTIAL_2__' },
=head2 CONSTRAINTS (optional)
You will most likely always have some constraints to use. These constraints
will be added to the WHERE clause of the SQL query, and will ideally reduce
the number of returned rows to one.
Any value that matches __CREDENTIAL_n__ (where n is a number) will be replaced with
the corresponding credential that was entered by the user. For an explanation of
what the credentials are and where they come from, see the section headed with
CREDENTIALS in L<CGI::Application::Plugin::Authentication>.
CONSTRAINTS => {
'users.email' => '__CREDENTIAL_1__',
'MD5:users.passphrase' => '__CREDENTIAL_2__',
'users.active' => 1,
}
=head2 ORDER_BY (optional)
lib/CGI/Application/Plugin/Authentication/Driver/DBI.pm view on Meta::CPAN
Here the password field is expected to be stored in the database in MD5 format. In order for the
MD5 check to work for all databases, the password will be encoded using perl, and then checked
against the value in the database. So in effect, the following will be done:
$username = 'test';
$password = '123';
$encoded_password = 'ICy5YqxZB1uWSwcVLSNLcA';
$sth = $dbh->prepare('SELECT count(*) FROM users WHERE username = ? AND password = ?';
$sth->execute($username, $encoded_password);
# I we found a row, then the user credentials are valid and the user is logged in
This is all automatically performed behind the scenes when you specify that a certain field
in the database is encoded.
We have to handle this slightly different when working with Unix crypt. In order to crypt
a password, you need to provide the crypt function with a 2 character salt value. These are
usually just generated randomly, and when the value is crypted, the first two characters of
the resulting string will be the 2 salt characters. The problem comes into play when you want
to check a password against a crypted password. You need to know the salt in order to
properly test the password. But in our case, the crypted password is in the DB. This means we
lib/CGI/Application/Plugin/Authentication/Driver/DBI.pm view on Meta::CPAN
COLUMNS => { 'crypt:password' => '__CREDENTIAL_2__' },
And here is what will happen behind the scenes:
$username = 'test';
$password = '123';
$sth = $dbh->prepare('SELECT password FROM users WHERE username = ?';
$sth->execute($username);
($encoded_password) = $sth->fetchrow_array;
if ($encoded_password eq crypt($password, $encoded_password)) {
# The credentials are valid and the user is logged in
}
Again, this is all done automatically behind the scenes, but I've included it here to illustrate how
the queries are performed, and how the comparisons are handled. For more information
see the section labelled ENCODED PASSWORDS in the L<CGI::Application::Plugin::Authentication::Driver>
docs.
=head1 EXAMPLE
# using multiple tables
# Here we check three credentials (user, password and domain) across
# two separate tables.
__PACKAGE__->authen->config(
DRIVER => [ 'DBI',
# the handle comes from $self->dbh, via the "DBH" plugin.
TABLES => ['user', 'domain'],
JOIN_ON => 'user.domainid = domain.id',
CONSTRAINTS => {
'user.name' => '__CREDENTIAL_1__',
'user.password' => '__CREDENTIAL_2__',
'domain.name' => '__CREDENTIAL_3__'
lib/CGI/Application/Plugin/Authentication/Driver/DBI.pm view on Meta::CPAN
COLUMNS => {
'crypt:U.password' => '__CREDENTIAL_2__'
},
],
);
=head1 METHODS
=head2 verify_credentials
This method will test the provided credentials against the values found in the database,
according to the Driver configuration.
=cut
sub verify_credentials {
my $self = shift;
my @creds = @_;
# verify that all the options are OK
my @_options = $self->options;
die "The DBI driver requires a hash of options" if @_options % 2;
my %options = @_options;
# Get a database handle - either one that is given to us, or see if there
# is a ->dbh method in the CGIApp module (This is provided by the
lib/CGI/Application/Plugin/Authentication/Driver/Dummy.pm view on Meta::CPAN
use base qw(CGI::Application);
use CGI::Application::Plugin::Authentication;
__PACKAGE__->authen->config(
DRIVER => 'Dummy',
);
=head1 DESCRIPTION
This Driver is the anti-authentication driver, since it doesn't check the
credentials at all, and just accepts whatever the user has entered.
It can be useful in development, or if you want a guest based system without
passwords.
=head1 EXAMPLE
__PACKAGE__->authen->config(
DRIVER => 'Dummy',
);
=head1 METHODS
=head2 verify_credentials
This method will automatically return the first credential as the username without checking
anything else.
=cut
sub verify_credentials {
my $self = shift;
return shift;
}
=head1 SEE ALSO
L<CGI::Application::Plugin::Authentication::Driver>, L<CGI::Application::Plugin::Authentication>, perl(1)
lib/CGI/Application/Plugin/Authentication/Driver/Generic.pm view on Meta::CPAN
['foobar.com', 'user1', '123'],
);
__PACKAGE__->authen->config(
DRIVER => [ 'Generic', \@users ],
CREDENTIALS => [ 'authen_domain', 'authen_username', 'authen_password' ]
);
- or -
sub check_password {
my @credentials = @_;
if ($credentials[0] eq 'test' && $credentials[1] eq 'secret') {
return 'testuser';
}
return;
}
__PACKAGE__->authen->config(
DRIVER => [ 'Generic', \&check_password ],
);
=head1 METHODS
=head2 verify_credentials
This method will test the provided credentials against either the hash ref, array ref or code ref
that the driver was configured with.
=cut
sub verify_credentials {
my $self = shift;
my @creds = @_;
my @options = $self->options;
my $data = $options[0];
if ( ref $data eq 'HASH' ) {
return undef unless( defined( $creds[0] ) && defined( $creds[1] ) );
return ( defined $data->{ $creds[0] } && $data->{ $creds[0] } eq $creds[1] ) ? $creds[0] : undef;
} elsif ( ref $data eq 'ARRAY' ) {
foreach my $row (@$data) {
lib/CGI/Application/Plugin/Authentication/Driver/HTPasswd.pm view on Meta::CPAN
=head1 DESCRIPTION
This Driver allows you to authenticate against an htpasswd file. For
information on the format of the htpasswd file, see the documentation for the
Apache webserver. This driver requires that the L<Apache::Htpasswd> module is
installed.
=head1 METHODS
=head2 verify_credentials
This method will test the provided credentials against the htpasswd file(s)
defined in the DRIVER config.
=cut
sub verify_credentials {
my $self = shift;
my ( $username, $password ) = @_;
# verify that all the options are OK
my @files = $self->options;
die "The HTPasswd driver requires at least one htpasswd file"
unless @files;
foreach my $file (@files) {
my $htpasswd = Apache::Htpasswd->new(
t/02_config.t view on Meta::CPAN
LOGIN_RUNMODE => 'login',
LOGOUT_RUNMODE => 'logout',
POST_LOGIN_RUNMODE => 'start',
CREDENTIALS => ['authen_username', 'authen_password'],
LOGIN_SESSION_TIMEOUT => '1h',
);
my $cgiapp=TestAppConfig->new;
lives_ok { $cgiapp->authen->config(%config) } 'All config parameters accepted';
is_deeply( $cgiapp->authen->credentials,[qw/authen_username authen_password/],'credentials set');
isa_ok($cgiapp->authen->drivers,'CGI::Application::Plugin::Authentication::Driver::Generic');
isa_ok($cgiapp->authen->store,'Store::Dummy');
%config = (
DRIVER => [ 'HTPassword', file => 't/htpasswd' ],
STORE => 'Store::Dummy',
LOGIN_URL => '/login.cgi',
LOGOUT_URL => '/',
POST_LOGIN_URL => '/protected/',
CREDENTIALS => ['authen_username', 'authen_password'],
t/03_authenticate.t view on Meta::CPAN
$ENV{CGI_APP_RETURN_ONLY} = 1;
# Missing Credentials
my $query =
CGI->new( { authen_username => 'user1', rm => 'two' } );
my $cgiapp = TestAppAuthenticate->new( QUERY => $query );
my $results = $cgiapp->run;
ok(!$cgiapp->authen->is_authenticated,'missing credentials - login failure');
is( $cgiapp->authen->username, undef, 'missing credentials - username not set' );
is( $cgiapp->param('post_login'),1,'missing credentials - POST_LOGIN_CALLBACK executed' );
# Successful Login
$query =
CGI->new( { authen_username => 'user1', authen_password => '123', rm => 'two' } );
$cgiapp = TestAppAuthenticate->new( QUERY => $query );
$results = $cgiapp->run;
ok($cgiapp->authen->is_authenticated,'successful login');
is( $cgiapp->authen->username, 'user1', 'successful login - username set' );
t/50_driver_undefined.t view on Meta::CPAN
my $self = shift;
my $count = $self->param('post_login') || 0;
$self->param( 'post_login' => $count + 1 );
}
}
$ENV{CGI_APP_RETURN_ONLY} = 1;
# Test 'find_options' function and what happens when we don't define 'verify_credentials'
{
local $cap_options->{DRIVER} = [
'Silly',
option1 => 'Tom',
option2 => 'Dick',
option3 => 'Harry'
];
my $query = CGI->new(
{
authen_username => 'user1',
t/50_driver_undefined.t view on Meta::CPAN
my $cgiapp = TestAppAuthenticate->new( QUERY => $query );
my @drivers = $cgiapp->authen->drivers;
ok( scalar(@drivers) == 1, 'We should have just one driver' );
ok( $drivers[0]->find_option( 'option1', 'Tom' ), 'Tom' );
ok( $drivers[0]->find_option( 'option2', 'Dick' ), 'Dick' );
ok( $drivers[0]->find_option( 'option3', 'Harry' ), 'Harry' );
throws_ok { $cgiapp->run }
qr/verify_credentials must be implemented in the subclass/,
'undefined function caught okay';
};
# Test what happens when we have no options.
{
local $cap_options->{DRIVER} = [ 'Silly', ];
my $query = CGI->new(
{
authen_username => 'user1',
rm => 'two',
t/50_driver_undefined.t view on Meta::CPAN
authen_password => '123',
destination => 'http://news.bbc.co.uk'
}
);
my $cgiapp = TestAppAuthenticate->new( QUERY => $query );
$cgiapp->run;
ok(!$cgiapp->authen->is_authenticated, "undefined username");
my @drivers = $cgiapp->authen->drivers;
ok(!defined($drivers[0]->verify_credentials(undef, 'blah')));
};
sub obfuscate {
my $param = shift || "G";
my $value = shift;
return "|$value|$param";
}
t/55_driver_authensimple.t view on Meta::CPAN
rm => 'protected',
authen_username => undef,
authen_password => '2234'
};
{
use CGI;
my $query = CGI->new( $params );
my $cgiapp = TestAppDriverAuthenSimple->new( QUERY => $query );
my @drivers = $cgiapp->authen->drivers;
ok(!defined $drivers[0]->verify_credentials(undef, '2234'), 'impossible case');
}
t/TestAppDriver.pm view on Meta::CPAN
return "username:$username\n";
}
sub protected {
my $self = shift;
my $username = $self->authen->username;
return "username:$username\n";
}
#
# These tests should pass with the credentials that were passed
# But other tests are performed that should successfully fail
#
sub run_authen_tests {
my $class = shift;
my $credentials = shift;
my @testdata = @_;
$ENV{CGI_APP_RETURN_ONLY} = 1;
foreach my $data (@testdata) {
my ($params, $query, $cgiapp, $results);
# Successful Login
$params = { map { $credentials->[$_] => $data->[$_] } 0..$#$credentials };
$params->{rm} = 'protected';
$query = CGI->new( $params );
$cgiapp = $class->new( QUERY => $query );
$results = $cgiapp->run;
ok($cgiapp->authen->is_authenticated,'successful login');
is( $cgiapp->authen->username, $data->[0], 'successful login - username set' );
# Missing Credentials
$params = { map { $credentials->[$_] => $data->[$_] } 1..$#$credentials };
$params->{rm} = 'protected';
$query = CGI->new( $params );
$cgiapp = $class->new( QUERY => $query );
$results = $cgiapp->run;
ok(!$cgiapp->authen->is_authenticated,'missing credentials - login failure');
is( $cgiapp->authen->username, undef, 'missing credentials - username not set' );
# Bad user or password
$params = { map { $credentials->[$_] => 'badvalue' } 0..$#$credentials };
$params->{rm} = 'protected';
$query = CGI->new( $params );
$cgiapp = $class->new( QUERY => $query );
$results = $cgiapp->run;
ok(!$cgiapp->authen->is_authenticated,'login failure');
is( $cgiapp->authen->username, undef, "login failure - username not set" );
}
}
#
# These tests should pass with the credentials that were passed
#
sub run_authen_success_tests {
my $class = shift;
my $credentials = shift;
my @testdata = @_;
$ENV{CGI_APP_RETURN_ONLY} = 1;
foreach my $data (@testdata) {
my ($params, $query, $cgiapp, $results);
# Failed Login
$params = { map { $credentials->[$_] => $data->[$_] } 0..$#$credentials };
$params->{rm} = 'protected';
$query = CGI->new( $params );
$cgiapp = $class->new( QUERY => $query );
$results = $cgiapp->run;
ok( $cgiapp->authen->is_authenticated,'good credentials - login success');
is( $cgiapp->authen->username, $data->[0], 'good credentials - username set' );
}
}
#
# These tests should fail with the credentials that were passed
#
sub run_authen_failure_tests {
my $class = shift;
my $credentials = shift;
my @testdata = @_;
$ENV{CGI_APP_RETURN_ONLY} = 1;
foreach my $data (@testdata) {
my ($params, $query, $cgiapp, $results);
# Failed Login
$params = { map { $credentials->[$_] => $data->[$_] } 0..$#$credentials };
$params->{rm} = 'protected';
$query = CGI->new( $params );
$cgiapp = $class->new( QUERY => $query );
$results = $cgiapp->run;
ok(!$cgiapp->authen->is_authenticated,'failed credentials - login failure');
is( $cgiapp->authen->username, undef, 'failed credentials - username not set' );
}
}
1;