CGI-Application-Plugin-Authentication

 view release on metacpan or  search on metacpan

lib/CGI/Application/Plugin/Authentication/Driver/DBI.pm  view on Meta::CPAN

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)

This option allows you to order the result set, in case the query returns
multiple rows.

     ORDER_BY => 'created DESC'

Note: This option is only useful if you also specify the COLUMNS option.

=head2 LIMIT  (optional)

In some situations your query may return multiple rows when you only want it to
return one.  For example if you insert and date a new row instead of updating
the existing row when the details for an account change.  In this case you want
the newest record from the result set, so it will be important to order the
result set and limit it to return only one row.

     LIMIT => 1

Note: This option is only useful if you also specify the COLUMNS option.

=head1 ENCODED PASSWORDS

It is quite common to store passwords in a database in some form that makes them hard
(or virtually impossible) to guess.  Most of the time one way encryption techniques
like Unix crypt or MD5 hashes are used to store the password securely (I would recommend
using MD5 or SHA1 over Unix crypt).  If you look at the examples listed above, you can
see that you can mark your columns with an encoding type.  Here is another example:

    CONSTRAINTS => {
        username       => '__CREDENTIAL_1__',
        'MD5:password' => '__CREDENTIAL_2__',
    }

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
can not generate the crypted test password before we run the query against the database.

So instead we pull the value of the crypted password out of the database, and then perform the
tests after the query, instead of before.  Here is an example:

    CONSTRAINTS => { 'username'       => '__CREDENTIAL_1__' },
    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.



( run in 2.282 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )