Apache-DBI

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

    [Philip M. Gollucci <pgollucci@p6m7g8.com>]

  - Fix a plethora of uninitialized variable warnings, 
    general code cleanup, don't import unneeded symbols
    from Carp, Digest::SHA1, and Digest::MD5
    [Philip M. Gollucci <pgollucci@p6m7g8.com>]

  - http://rt.cpan.org/Ticket/Display.html?id=17073
    $sth->rows is inconsistent across DBD::* drivers
    and sometimes always returns 0.  We were using 
    this to distinguish between a blank password and
    no passwd.  Now we don't call this function.

    Reported by: rkimmelmann@web.de 
    [Philip M. Gollucci <pgollucci@p6m7g8.com>]
  
  - http://rt.cpan.org/Ticket/Display.html?id=17422
    a fatal error involving mp1, mp2 constants co-existance 
    was fixed in AuthDBI.
    [Philip M. Gollucci <pgollucci@p6m7g8.com>]

Changes  view on Meta::CPAN


	- in AuthDBI remove check of configured data_source in order to allow 
	  the usage of the environment variable DBI_DSN. Bug spotted by 
	  Oleg Bartunov <oleg@sai.msu.su>.

	- applied patch from Matt Loschert <loschert@servint.com>,
	  which avoids 'Use of uninitialized value ...' in Apache::DBI.

	- added new attribute 'Auth_DBI_encryption_salt' as proposed by
	  Nathan Clemons <nathan@windsofstorm.net>.
	  Per default this is set to 'password' which will use the password 
	  as salt for the crypt function. Setting this to 'userid' will use 
	  the userid as salt.

	- fixed bug with setting Auth_DBI_nopasswd to 'on', spotted by 
	  "Sigurjon Olafsson" <sigurjon@gm.is>.

0.85 August 24, 1999

	- change separator of Auth_DBI_data_source, Auth_DBI_username and 
	  Auth_DBI_password from comma to tilde, in order to avoid clashes 
	  with embedded attributes in data_source. 
	  Bug spotted by Oleg Bartunov <oleg@sai.msu.su>.

	- applied patch to Apache::DBI.pm from Tim Bunce <Tim.Bunce@ig.co.uk>
	  which solves the problem that Apache::DBI did not return a ref cursor.

0.84  August 21, 1999

	- combine Apache::AuthenDBI and Apache::AuthzDBI into one package 
	  Apache::AuthDBI.

Changes  view on Meta::CPAN


	- connect attributes for authentication and authorization may be a 
	  list of several servers, all of which will be used until the first 
	  connect succeeds.
	  Proposed by Matt Loschert <loschert@servint.com>.

	- the PerlCleanupHandler in Apache::DBI.pm, which is supposed 
	  to initiate a rollback in case AutoCommit is off, will only be 
	  created, if the initial data_source sets AutoCommit to 0.

	- fixed bug with empty password, which didn't fall through for
	  authoritative = off, spotted by "Graham Johnson" <graham@iii.co.uk>.

	- analogous to the environment variables REMOTE_GROUPS and REMOTE_GROUP 
	  the selected passwords and the matched password are put into the 
	  environment variables REMOTE_PASSWORDS and REMOTE_PASSWORD. 
	  Proposed by Jochen Wiedmann <joe@ispsoft.de>.

	- add traces.txt, which serves as reference for the debug output. 

0.83  August 08, 1999

	- make ping configurable, proposed by 
	  Gunther Birznieks <gunther@nhgri.nih.gov>

	- change $user_sent_quoted to $user_sent when checking for 
	  placeholders (Michael Smith <mjs@iii.co.uk>)

	- bug-fix for encrypted passwords, which have never been taken
	  from the cache. Spotted by Yves BLUSSEAU <yves.blusseau@sncf.fr>.

0.82  June 03, 1999

	- bug-fix spotted by "Dale Manemann" <manemann@dubuque.net>:
	  correct the password handling for the case, where the password has
	  been changed in the database and the old password is still cached.

	- proposal from Honza Pazdziora <adelton@informatics.muni.cz>:
	  add PerlCleanupHandler in Apache::DBI, which issues a rollback 
	  unless AutoCommit is on. 

	- changed behavior of AuthzDBI: the first match of a
	  requirement is sufficient for successful authorization.
	  Prior to this release, all requirement lines had to 
	  be fulfilled. 

Changes  view on Meta::CPAN

	  for the select statements. This still keeps the userid entries
	  in the cache unique, but solves the problem with different 
	  AuthNames which eventually forces the user to authenticate 
	  several times. 

	- new configuration option Auth_DBI_expeditive from
	  "Jordi 'Matematic' Salvat" <jordi@webarna.com>. 
	  When authorization fails, AuthzDBI returns AUTH_REQUIRED
	  as default. With Auth_DBI_expeditive set to "on" it returns
	  FORBIDDEN if access is denied. Hence this can be distinguished 
	  from the case, where the user just mistyped the password.

	- applied patch from Ask Bjoern Hansen <ask@valueclick.com>:
	  get rid of some annoying "Use of uninitialized value ..."

	- applied patch from Joshua Chamas <joshua@chamas.com>:
	  use eval{ping} to prevent using an invalid database handle.

	- added 'use Apache;' to Apache::DBI.pm as proposed by
    Michael Smith <mjs@iii.co.uk>.

	- implemented multiple passwords per userid as proposed by
	  dan hammer <dhammer@verio.net>.

	- applied patch for case-insensitive user-ids from 
	  <grussell@wiley.com>. 

	- implement proposal from Honza Pazdziora <adelton@informatics.muni.cz>:
	  Auth_DBI_casesensitive replaced by Auth_DBI_uidcasesensitive and 
	  Auth_DBI_pwdcasesensitive. 

	- applied patch from fdc@cliwe.ping.de (Frank D. Cringle):

Changes  view on Meta::CPAN

  - applied patches from Doug MacEachern:
    o new method  Apache::DBI->connect_on_init()
    o set environment variable REMOTE_GROUP in AuthzDBI.pm.

0.76 December 18, 1997

	- removed unused variable from AuthzDBI.pm

0.75 November 02, 1997

	- strip trailing blanks from password for 
	  fixed-length data type

	- new token: 'Auth_DBI_casesensitive'
    fixed bug when using attributes in connect method
    fixed bug which appeared with perl5.004_04
	  (Hakan Tandogan <hakan@iconsult.com>

0.74 August 15, 1997

  - new module: AuthzDBI for Authorization,

README  view on Meta::CPAN

an embedded perl interpreter like mod_perl. They provide support for basic 
authentication and authorization as well as support for persistent database 
connections via Perl's Database Independent Interface (DBI). 

o DBI.pm provides persistent database connections: 
  - connections can be established during server-startup 
  - configurable rollback to ensure data integrity 
  - configurable verification of the connections to avoid time-outs. 

o AuthDBI.pm provides authentication and authorization: 
  - optional shared cache for passwords to minimize database load 
  - configurable cleanup-handler deletes outdated entries from the cache 

Apache::DBI has been in widespread deployment on many platforms for
years.  Apache::DBI is one of the most widely used mod_perl related
modules.  It can be considered stable.



RECENT CHANGES:
---------------

README  view on Meta::CPAN


URL/.htaccess:

  AuthName DBI
  AuthType Basic

  PerlAuthenHandler Apache::AuthDBI::authen

  PerlSetVar Auth_DBI_data_source   dbi:driver:dsn
  PerlSetVar Auth_DBI_username      db_username
  PerlSetVar Auth_DBI_password      db_password
  # DBI->connect($data_source, $username, $password)

  PerlSetVar Auth_DBI_pwd_table     users
  PerlSetVar Auth_DBI_uid_field     username
  PerlSetVar Auth_DBI_pwd_field     password
  #SELECT pwd_field FROM pwd_table WHERE uid_field=$user

  require user myuser

In this example it is assumed, that your database contains a table named 
'users' which has at least the two columns 'username' and 'password'. When 
accessing the URL for the first time a requester pops up, asking for username 
and password. For authentication the module retrieves for the given username 
the password from the database. This is compared with the crypted password 
given by the user. If the check succeeds, the user is given access to the 
specified URL. 

Please do not confuse this user authentication with the username/password 
needed for the database connect. These two authentications are completely 
independent !

Windows users should turn off the case-sensitive option.


2. group authorization

Suppose you want to restrict access to a certain URL to a specific user group 
and the necessary information for restricting user access is stored in your 

README  view on Meta::CPAN

URL/.htaccess:

  AuthName DBI
  AuthType Basic

  PerlAuthenHandler Apache::AuthDBI::authen
  PerlAuthzHandler  Apache::AuthDBI::authz

  PerlSetVar Auth_DBI_data_source   dbi:mydriver:mydsn
  PerlSetVar Auth_DBI_username      db_username
  PerlSetVar Auth_DBI_password      db_password
  # DBI->connect($data_source, $username, $password)

  PerlSetVar Auth_DBI_pwd_table     users
  PerlSetVar Auth_DBI_uid_field     username
  PerlSetVar Auth_DBI_pwd_field     password
  PerlSetVar Auth_DBI_grp_field     groupname
  #SELECT grp_field FROM pwd_table WHERE uid_field=$user

  require group mygroup

In this example it is assumed, that your database contains a table named 
'users' which has at least the three columns 'username', 'password' and 
'groupname'. When accessing the URL for the first time a requester pops up, 
asking for username and password. The first check (authentication) retrieves 
for the given username the password from the database. This is compared with 
the crypted password given by the user. In a second check (authorization) 
the groups of the given username are looked up in the database and compared 
with the groups required in the .htaccess file. If both checks succeed, the 
user is given access to the specified URL. 

Please do not confuse the user authentication with the username/password 
needed for the database connect. These two authentications are completely 
independent ! 

Although authorization handles all types of basic authentication it is 
perfectly sufficient to configure only authentication, as long, as the 
require token restricts access to 'valid-user' or to one or more single user 
names. You need to configure authorization only if you have more than one 
require token or if the require token contains one or more group names. 


3. persistent database connection

The following information is intended to motivate the use of persistent 
database connections and to explain the necessary configuration. 

In the above example for user authorization the requester asking for username 
and password pops up only once. The browser stores the user input and provides 
it to subsequent requests. But the sequence of two database accesses is done 
for every request, e.g. if your restricted URL contains a HTML page with some 
images, this sequence is executed once for the HTML page and once for every 
image ! For databases which needs a significant amount of time for the connect 
(e.g. start of a backend process) this might become an unacceptable overhead 
for the authorization procedure. This drawback can be overcome with the use of 
persistent database connections as provided by the Apache::DBI module. 

The benefit of a persistent database connection is not limited to the use 
of authorization. Every application, which does a lot of database queries, 

lib/Apache/AuthDBI.pm  view on Meta::CPAN

  else {
    Apache->push_handlers(@_);
  }
}

# configuration attributes, defaults will be overwritten with values
# from .htaccess.
my %Config = (
              'Auth_DBI_data_source'      => '',
              'Auth_DBI_username'         => '',
              'Auth_DBI_password'         => '',
              'Auth_DBI_pwd_table'        => '',
              'Auth_DBI_uid_field'        => '',
              'Auth_DBI_pwd_field'        => '',
              'Auth_DBI_pwd_whereclause'  => '',
              'Auth_DBI_grp_table'        => '',
              'Auth_DBI_grp_field'        => '',
              'Auth_DBI_grp_whereclause'  => '',
              'Auth_DBI_log_field'        => '',
              'Auth_DBI_log_string'       => '',
              'Auth_DBI_authoritative'    => 'on',
              'Auth_DBI_nopasswd'         => 'off',
              'Auth_DBI_encrypted'        => 'on',
              'Auth_DBI_encryption_salt'  => 'password',
              #Using Two (or more) Methods Will Allow for Fallback to older Methods
              'Auth_DBI_encryption_method'=> 'sha1hex/md5/crypt',
              'Auth_DBI_uidcasesensitive' => 'on',
              'Auth_DBI_pwdcasesensitive' => 'on',
              'Auth_DBI_placeholder'      => 'off',
              'Auth_DBI_expeditive'       => 'on',
             );

# stores the configuration of current URL.
# initialized  during authentication, eventually re-used for authorization.
my $Attr = {};

# global cache: all records are put into one string.
# record separator is a newline. Field separator is $;.
# every record is a list of id, time of last access, password, groups
#(authorization only).
# the id is a comma separated list of user_id, data_source, pwd_table,
# uid_field.
# the first record is a timestamp, which indicates the last run of the
# CleanupHandler followed by the child counter.
my $Cache = time . "$;0\n";

# unique id which serves as key in $Cache.
# the id is generated during authentication and re-used for authorization.
my $ID;

lib/Apache/AuthDBI.pm  view on Meta::CPAN

          $type .= 'main'     if $r->is_main;
        }
        debug (1, "==========\n$prefix request type = >$type<");
    }

    return MP2 ? Apache2::Const::OK() : Apache::Constants::OK()
        unless $r->is_initial_req; # only the first internal request

    debug (2, "REQUEST:" . $r->as_string);

    # here the dialog pops up and asks you for username and password
    my ($res, $passwd_sent) = $r->get_basic_auth_pw;
    {
      no warnings qw(uninitialized);
      debug (2, "$prefix get_basic_auth_pw: res = >$res<, password sent = >$passwd_sent<");
    }
    return $res if $res; # e.g. HTTP_UNAUTHORIZED

    # get username
    my $user_sent = $r->user;
    debug(2, "$prefix user sent = >$user_sent<");

    # do we use shared memory for the global cache ?
    debug (2, "$prefix cache in shared memory, shmid $SHMID, shmsize $SHMSIZE, semid $SEMID");

lib/Apache/AuthDBI.pm  view on Meta::CPAN

    while(($key, $val) = each %Config) {
        $val = $r->dir_config($key) || $val;
        $key =~ s/^Auth_DBI_//;
        $Attr->{$key} = $val;
        debug(2, sprintf("$prefix Config{ %-16s } = %s", $key, $val));
    }

    # parse connect attributes, which may be tilde separated lists
    my @data_sources = split /~/, $Attr->{data_source};
    my @usernames    = split /~/, $Attr->{username};
    my @passwords    = split /~/, $Attr->{password};
    # use ENV{DBI_DSN} if not defined
    $data_sources[0] = '' unless $data_sources[0];

    # obtain the id for the cache
    # remove any embedded attributes, because of trouble with regexps
    my $data_src = $Attr->{data_source};
    $data_src =~ s/\(.+\)//g;

    $ID = join ',',
        $user_sent, $data_src, $Attr->{pwd_table}, $Attr->{uid_field};

lib/Apache/AuthDBI.pm  view on Meta::CPAN

    unless ($Attr->{pwd_table} && $Attr->{uid_field} && $Attr->{pwd_field}) {
        debug (2, "$prefix not configured, return DECLINED");
        return MP2 ? Apache2::Const::DECLINED() :
            Apache::Constants::DECLINED();
    }

    # do we want Windows-like case-insensitivity?
    $user_sent   = lc $user_sent   if $Attr->{uidcasesensitive} eq "off";
    $passwd_sent = lc $passwd_sent if $Attr->{pwdcasesensitive} eq "off";

    # check whether the user is cached but consider that the password
    # possibly has changed
    my $passwd = '';
    if ($CacheTime) { # do we use the cache ?
        if ($SHMID) { # do we keep the cache in shared memory ?
            semop($SEMID, $obtain_lock)
                or warn "$prefix semop failed \n";
            shmread($SHMID, $Cache, 0, $SHMSIZE)
                or warn "$prefix shmread failed \n";
            substr($Cache, index($Cache, "\0")) = '';
            semop($SEMID, $release_lock) 

lib/Apache/AuthDBI.pm  view on Meta::CPAN

            $last_access   = $1;
            $passwd_cached = $2;
            $groups_cached = $3;
            debug(2, "$prefix cache: found >$ID< >$last_access< >$passwd_cached<");

            my @passwds_to_check =
                &get_passwds_to_check(
                                      $Attr,
                                      user_sent   => $user_sent,
                                      passwd_sent => $passwd_sent,
                                      password    => $passwd_cached
                                     );

            debug(2, "$prefix " . scalar(@passwds_to_check) . " passwords to check");
            foreach my $passwd_to_check (@passwds_to_check) {
              # match cached password with password sent
              $passwd = $passwd_cached if $passwd_to_check eq $passwd_cached;
              last if $passwd;
            }
        }
    }

    # found in cache
    if ($passwd) {
        debug(2, "$prefix passwd found in cache");
    }
    else {
        # password not cached or changed
        debug (2, "$prefix passwd not found in cache");

        # connect to database, use all data_sources until the connect succeeds
        for (my $j = 0; $j <= $#data_sources; $j++) {
            last if (
                     $dbh = DBI->connect(
                                         $data_sources[$j],
                                         $usernames[$j],
                                         $passwords[$j]
                                        )
                    );
        }
        unless ($dbh) {
            $r->log_reason(
                           "$prefix db connect error with data_source " .
                           ">$Attr->{data_source}<: $DBI::errstr",
                           $r->uri
                          );
            return MP2 ? Apache2::Const::SERVER_ERROR() :

lib/Apache/AuthDBI.pm  view on Meta::CPAN

        # execute statement
        my $rv;
        unless ($rv = ($Attr->{placeholder} eq "on") ?
                $sth->execute($user_sent) : $sth->execute) {
            $r->log_reason("$prefix can not execute statement: $DBI::errstr", $r->uri);
            $dbh->disconnect;
            return MP2 ? Apache2::Const::SERVER_ERROR() :
                Apache::Constants::SERVER_ERROR();
        }

        my $password;
        $sth->execute();
        $sth->bind_columns(\$password);
        my $cnt = 0;
        while ($sth->fetch()) {
            $password =~ s/ +$// if $password;
            $passwd .= "$password$;";
            $cnt++;
        }

        chop $passwd if $passwd;
        # so we can distinguish later on between no password and empty password
        undef $passwd if 0 == $cnt;

        if ($sth->err) {
            $dbh->disconnect;
            return MP2 ? Apache2::Const::SERVER_ERROR() :
                Apache::Constants::SERVER_ERROR();
        }
        $sth->finish;

        # re-use dbh for logging option below
        $dbh->disconnect unless $Attr->{log_field} && $Attr->{log_string};
    }

    $r->subprocess_env(REMOTE_PASSWORDS => $passwd);
    debug(2, "$prefix passwd = >$passwd<");

    # check if password is needed
    unless ($passwd) { # not found in database
        # if authoritative insist that user is in database
        if ($Attr->{authoritative} eq 'on') {
            $r->log_reason("$prefix password for user $user_sent not found", $r->uri);
            $r->note_basic_auth_failure;
            return MP2 ? Apache2::Const::AUTH_REQUIRED() :
                Apache::Constants::AUTH_REQUIRED();
        }
        else {
            # else pass control to the next authentication module
            return MP2 ? Apache2::Const::DECLINED() :
                Apache::Constants::DECLINED();
        }
    }

    # allow any password if nopasswd = on and the retrieved password is empty
    if ($Attr->{nopasswd} eq 'on' && !$passwd) {
        return MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
    }

    # if nopasswd is off, reject user
    unless ($passwd_sent && $passwd) {
        $r->log_reason("$prefix user $user_sent: empty password(s) rejected", $r->uri);
        $r->note_basic_auth_failure;
        return MP2 ? Apache2::Const::AUTH_REQUIRED() :
            Apache::Constants::AUTH_REQUIRED();
    }

    # compare passwords
    my $found = 0;
    foreach my $password (split /$;/, $passwd) {
        # compare all the passwords using as many encryption methods
        # in fallback as needed
        my @passwds_to_check =
            &get_passwds_to_check(
                                  $Attr,
                                  user_sent   => $user_sent,
                                  passwd_sent => $passwd_sent,
                                  password    => $password
                                 );

        debug (2, "$prefix " . scalar(@passwds_to_check) . " passwords to check");

        foreach my $passwd_to_check (@passwds_to_check) {
          debug(
                2,
                "$prefix user $user_sent: Password after Preparation " .
                ">$passwd_to_check< - trying for a match with >$password<"
               );

          if ($passwd_to_check eq $password) {
              $found = 1;
              $r->subprocess_env(REMOTE_PASSWORD => $password);
              debug (
                     2,
                     "$prefix user $user_sent: Password from Web Server " .
                     ">$passwd_sent< - Password after Preparation >$passwd_to_check< - " .
                     "password match for >$password<"
                    );

            # update timestamp and cache userid/password if CacheTime
            # is configured
            if ($CacheTime) { # do we use the cache ?
                if ($SHMID) { # do we keep the cache in shared memory ?
                    semop($SEMID, $obtain_lock)
                        or warn "$prefix semop failed \n";
                    shmread($SHMID, $Cache, 0, $SHMSIZE)
                        or warn "$prefix shmread failed \n";
                    substr($Cache, index($Cache, "\0")) = '';
                }

                # update timestamp and password or append new record
                my $now = time;
                if (!($Cache =~ s/$ID$;\d+$;.*$;(.*)\n/$ID$;$now$;$password$;$1\n/)) {
                    $Cache .= "$ID$;$now$;$password$;\n";
                }

                if ($SHMID) { # write cache to shared memory
                    shmwrite($SHMID, $Cache, 0, $SHMSIZE)
                        or warn "$prefix shmwrite failed \n";
                    semop($SEMID, $release_lock)
                        or warn "$prefix semop failed \n";
                }
            }
            last;
          }
        }

        #if the passwd matched (encrypted or otherwise), don't check the
        # myriad other passwords that may or may not exist
        last if $found > 0 ;
    }

    unless ($found) {
        $r->log_reason("$prefix user $user_sent: password mismatch", $r->uri);
        $r->note_basic_auth_failure;
        return MP2 ? Apache2::Const::AUTH_REQUIRED() :
            Apache::Constants::AUTH_REQUIRED();
    }

    # logging option
    if ($Attr->{log_field} && $Attr->{log_string}) {
        if (!$dbh) { # connect to database if not already done
            my $connect;
            for (my $j = 0; $j <= $#data_sources; $j++) {
                if ($dbh = DBI->connect(
                                        $data_sources[$j],
                                        $usernames[$j],
                                        $passwords[$j]
                                       )) {
                    $connect = 1;
                    last;
                }
            }
            unless ($connect) {
                $r->log_reason("$prefix db connect error with $Attr->{data_source}", $r->uri);
                return MP2 ? Apache2::Const::SERVER_ERROR() :
                    Apache::Constants::SERVER_ERROR();
            }

lib/Apache/AuthDBI.pm  view on Meta::CPAN

        if ($diff > $CleanupTime) {
            debug (2, "$prefix push PerlCleanupHandler");
            push_handlers(PerlCleanupHandler => \&cleanup);
        }
    }

    debug (2, "$prefix return OK\n");
    return MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
}

#Encrypts a password in all supported/requested methods and passes back
#array for comparison
sub get_passwds_to_check {
    my $Attr = shift;
    my %params = @_;


    my ($prefix) = "$$ Apache::AuthDBI::get_passwds_to_check ";

    my ($salt, @passwds_to_check);

lib/Apache/AuthDBI.pm  view on Meta::CPAN

        if ($Attr->{encryption_method} =~ /(^|\/)md5hex($|\/)/i) {
            push @passwds_to_check, MD5_digest(
                                               text  => $params{'passwd_sent'},
                                               format => 'hex'
                                              );
        }

        #CRYPT
        if ($Attr->{encryption_method} =~ /(^|\/)crypt($|\/)/i) {
            $salt = $Attr->{encryption_salt} eq 'userid' ?
                $params{'user_sent'} : $params{'password'};
            #Bug Fix in v0.94 (marked as 0.93 in file.  salt was NOT being sent
            # to crypt) - KAM - 06-16-2005
            push @passwds_to_check, crypt($params{'passwd_sent'}, $salt);
        }

        #WE DIDN'T GET ANY PASSWORDS TO CHECK.  MUST BE A PROBLEM
        if (scalar(@passwds_to_check) < 1) {
            debug (2, "$prefix Error: No Valid Encryption Method Specified");
        }
    }

lib/Apache/AuthDBI.pm  view on Meta::CPAN

    # get username
    my $user_sent = $r->user;
    debug(2, "$prefix user sent = >$user_sent<");

    # here we could read the configuration, but we re-use the configuration
    # from the authentication

    # parse connect attributes, which may be tilde separated lists
    my @data_sources = split /~/, $Attr->{data_source};
    my @usernames    = split /~/, $Attr->{username};
    my @passwords    = split /~/, $Attr->{password};
    # use ENV{DBI_DSN} if not defined
    $data_sources[0] = '' unless $data_sources[0];

    # if not configured decline
    unless ($Attr->{pwd_table} && $Attr->{uid_field} && $Attr->{grp_field}) {
        debug(2, "$prefix not configured, return DECLINED");
        return MP2 ? Apache2::Const::DECLINED() :
            Apache::Constants::DECLINED();
    }

lib/Apache/AuthDBI.pm  view on Meta::CPAN

            # groups not cached or changed
            debug(2, "$prefix groups not found in cache");

            # connect to database, use all data_sources until the connect
            # succeeds
            my $connect;
            for (my $j = 0; $j <= $#data_sources; $j++) {
                if ($dbh = DBI->connect(
                                        $data_sources[$j],
                                        $usernames[$j],
                                        $passwords[$j]
                                       )) {
                    $connect = 1;
                    last;
                }
            }
            unless ($connect) {
                $r->log_reason(
                               "$prefix db connect error with " .
                               "$Attr->{data_source}",
                               $r->uri

lib/Apache/AuthDBI.pm  view on Meta::CPAN

 # Authentication and Authorization in .htaccess:

 AuthName DBI
 AuthType Basic

 PerlAuthenHandler Apache::AuthDBI::authen
 PerlAuthzHandler  Apache::AuthDBI::authz

 PerlSetVar Auth_DBI_data_source   dbi:driver:dsn
 PerlSetVar Auth_DBI_username      db_username
 PerlSetVar Auth_DBI_password      db_password
 #DBI->connect($data_source, $username, $password)

 PerlSetVar Auth_DBI_pwd_table     users
 PerlSetVar Auth_DBI_uid_field     username
 PerlSetVar Auth_DBI_pwd_field     password
 # authentication: SELECT pwd_field FROM pwd_table WHERE uid_field=$user
 PerlSetVar Auth_DBI_grp_field     groupname
 # authorization: SELECT grp_field FROM pwd_table WHERE uid_field=$user

 require valid-user
 require user   user_1  user_2 ...
 require group group_1 group_2 ...

The AuthType is limited to Basic. You may use one or more valid require lines.
For a single require line with the requirement 'valid-user' or with the

lib/Apache/AuthDBI.pm  view on Meta::CPAN


=head1 DESCRIPTION

This module allows authentication and authorization against a database
using Perl's DBI. For supported DBI drivers see:

 http://dbi.perl.org/

Authentication:

For the given username the password is looked up in the cache. If the cache
is not configured or if the user is not found in the cache, or if the given
password does not match the cached password, it is requested from the database.

If the username does not exist and the authoritative directive is set to 'on',
the request is rejected. If the authoritative directive is set to 'off', the
control is passed on to next module in line.

If the password from the database for the given username is empty and the
nopasswd directive is set to 'off', the request is rejected. If the nopasswd
directive is set to 'on', any password is accepted.

Finally the passwords (multiple passwords per userid are allowed) are
retrieved from the database. The result is put into the environment variable
REMOTE_PASSWORDS. Then it is compared to the password given. If the encrypted
directive is set to 'on', the given password is encrypted using perl's crypt()
function before comparison. If the encrypted directive is set to 'off' the
plain-text passwords are compared.

If this comparison fails the request is rejected, otherwise the request is
accepted and the password is put into the environment variable REMOTE_PASSWORD.

The SQL-select used for retrieving the passwords is as follows:

 SELECT pwd_field FROM pwd_table WHERE uid_field = user

If a pwd_whereclause exists, it is appended to the SQL-select.

This module supports in addition a simple kind of logging mechanism. Whenever
the handler is called and a log_string is configured, the log_field will be
updated with the log_string. As log_string - depending upon the database -
macros like TODAY can be used.

The SQL-select used for the logging mechanism is as follows:

 UPDATE pwd_table SET log_field = log_string WHERE uid_field = user

Authorization:

When the authorization handler is called, the authentication has already been
done. This means, that the given username/password has been validated.

The handler analyzes and processes the requirements line by line. The request
is accepted if the first requirement is fulfilled.

In case of 'valid-user' the request is accepted.

In case of one or more user-names, they are compared with the given user-name
until the first match.

In case of one or more group-names, all groups of the given username are

lib/Apache/AuthDBI.pm  view on Meta::CPAN

in an extra table, if there is an m:n relationship between users and groups.
From all selected groups a comma-separated list is build, which is compared
with the required groups. If you don't like normalized group records you can
put such a comma-separated list of groups (no spaces) into the grp_field
instead of single groups.

If a grp_whereclause exists, it is appended to the SQL-select.

Cache:

The module maintains an optional cash for all passwords/groups. See the
method setCacheTime(n) on how to enable the cache. Every server has it's
own cache. Optionally the cache can be put into a shared memory segment,
so that it can be shared among all servers. See the CONFIGURATION section
on how to enable the usage of shared memory.

In order to prevent the cache from growing indefinitely a CleanupHandler can
be initialized, which skips through the cache and deletes all outdated entries.
This can be done once per request after sending the response, hence without
slowing down response time to the client. The minimum time between two
successive runs of the CleanupHandler is configurable (see the CONFIGURATION
section). The default is 0, which runs the CleanupHandler after every request.

=head1 LIST OF TOKENS

=over

=item * Auth_DBI_data_source (Authentication and Authorization)

The data_source value has the syntax 'dbi:driver:dsn'. This parameter is
passed to the database driver for processing during connect. The data_source
parameter (as well as the username and the password parameters) may be a
tilde ('~') separated list of several data_sources. All of these triples will
be used until a successful connect is made. This way several backup-servers
can be configured. if you want to use the environment variable DBI_DSN
instead of a data_source, do not specify this parameter at all.

=item * Auth_DBI_username (Authentication and Authorization)

The username argument is passed to the database driver for processing during
connect. This parameter may be a tilde ('~') separated list.
See the data_source parameter above for the usage of a list.

=item * Auth_DBI_password (Authentication and Authorization)

The password argument is passed to the database driver for processing during
connect. This parameter may be a tilde ('~')  separated list.
See the data_source parameter above for the usage of a list.

=item * Auth_DBI_pwd_table (Authentication and Authorization)

Contains at least the fields with the username and the (possibly encrypted)
password. The username should be unique.

=item * Auth_DBI_uid_field (Authentication and Authorization)

Field name containing the username in the Auth_DBI_pwd_table.

=item * Auth_DBI_pwd_field (Authentication only)

Field name containing the password in the Auth_DBI_pwd_table.

=item * Auth_DBI_pwd_whereclause (Authentication only)

Use this option for specifying more constraints to the SQL-select.

=item * Auth_DBI_grp_table (Authorization only)

Contains at least the fields with the username and the groupname.

=item * Auth_DBI_grp_field (Authorization only)

lib/Apache/AuthDBI.pm  view on Meta::CPAN


=item * Auth_DBI_authoritative  < on / off> (Authentication and Authorization)

Default is 'on'. When set 'on', there is no fall-through to other
authentication methods if the authentication check fails. When this directive
is set to 'off', control is passed on to any other authentication modules. Be
sure you know what you are doing when you decide to switch it off.

=item * Auth_DBI_nopasswd  < on / off > (Authentication only)

Default is 'off'. When set 'on' the password comparison is skipped if the
password retrieved from the database is empty, i.e. allow any password.
This is 'off' by default to ensure that an empty Auth_DBI_pwd_field does not 
allow people to log in with a random password. Be sure you know what you are 
doing when you decide to switch it on.

=item * Auth_DBI_encrypted  < on / off > (Authentication only)

Default is 'on'. When set to 'on', the password retrieved from the database
is assumed to be crypted. Hence the incoming password will be crypted before
comparison. When this directive is set to 'off', the comparison is done
directly with the plain-text entered password.

=item *
Auth_DBI_encryption_method < sha1hex/md5hex/crypt > (Authentication only)

Default is blank. When set to one or more encryption method, the password
retrieved from the database is assumed to be crypted. Hence the incoming
password will be crypted before comparison.  The method supports falling
back so specifying 'sha1hex/md5hex' would allow for a site that is upgrading 
to sha1 to support both methods.  sha1 is the recommended method.

=item * Auth_DBI_encryption_salt < password / userid > (Authentication only)

When crypting the given password AuthDBI uses per default the password
selected from the database as salt. Setting this parameter to 'userid',
the module uses the userid as salt.

=item *
Auth_DBI_uidcasesensitive  < on / off > (Authentication and Authorization)

Default is 'on'. When set 'off', the entered userid is converted to lower case.
Also the userid in the password select-statement is converted to lower case.

=item * Auth_DBI_pwdcasesensitive  < on / off > (Authentication only)

Default is 'on'. When set 'off', the entered password is converted to lower
case.

=item * Auth_DBI_placeholder < on / off > (Authentication and Authorization)

Default is 'off'.  When set 'on', the select statement is prepared using a
placeholder for the username.  This may result in improved performance for
databases supporting this method.

=back

lib/Apache/AuthDBI.pm  view on Meta::CPAN

A common usage is to load the module in a startup file via the PerlRequire
directive. See eg/startup.pl for an example.

There are three configurations which are server-specific and which can be done
in a startup file:

 Apache::AuthDBI->setCacheTime(0);

This configures the lifetime in seconds for the entries in the cache.
Default is 0, which turns off the cache. When set to any value n > 0, the
passwords/groups of all users will be cached for at least n seconds. After
finishing the request, a special handler skips through the cache and deletes
all outdated entries (entries, which are older than the CacheTime).

 Apache::AuthDBI->setCleanupTime(-1);

This configures the minimum time in seconds between two successive runs of the
CleanupHandler, which deletes all outdated entries from the cache. The default
is -1, which disables the CleanupHandler. Setting the interval to 0 runs the
CleanupHandler after every request. For a heavily loaded server this should be
set to a value, which reflects a compromise between scanning a large cache

lib/Apache/AuthDBI.pm  view on Meta::CPAN

and that mod_perl needs to be configured with the appropriate call-back hooks:

  PERL_AUTHEN=1 PERL_AUTHZ=1 PERL_CLEANUP=1 PERL_STACKED_HANDLERS=1

Apache::DBI v0.94 was the last version before dual mod_perl 2.x support was begun.
It still recommened that you use the latest version of Apache::DBI because Apache::DBI
versions less than 1.00 are NO longer supported.

=head1 SECURITY

In some cases it is more secure not to put the username and the password in
the .htaccess file. The following example shows a solution to this problem:

httpd.conf:

 <Perl>
 my($uid,$pwd) = My::dbi_pwd_fetch();
 $Location{'/foo/bar'}->{PerlSetVar} = [
     [ Auth_DBI_username  => $uid ],
     [ Auth_DBI_password  => $pwd ],
 ];
 </Perl>


=head1 SEE ALSO

L<Apache>, L<mod_perl>, L<DBI>

=head1 AUTHORS

traces.txt  view on Meta::CPAN

-------------------------------------------------------------------


2. Apache::AuthDBI::authen
--------------------------

2.1. normal authentication, setCacheTime(60), no cleanup handler

     ==========
     25362 Apache::AuthDBI::authen request type = >initial main< 
     25362 Apache::AuthDBI::authen get_basic_auth_pw: res = >401<, password sent = ><
     -------------- here the password requester of the browser pops up --------
     ==========
     25364 Apache::AuthDBI::authen request type = >initial main< 
     25364 Apache::AuthDBI::authen get_basic_auth_pw: res = >0<, password sent = >support<
     25364 Apache::AuthDBI::authen user sent = >support<
     25364 Apache::AuthDBI::authen Config{ pwdcasesensitive } = on
     25364 Apache::AuthDBI::authen Config{ pwd_whereclause  } = 
     25364 Apache::AuthDBI::authen Config{ placeholder      } = off
     25364 Apache::AuthDBI::authen Config{ log_field        } = 
     25364 Apache::AuthDBI::authen Config{ uid_field        } = userid
     25364 Apache::AuthDBI::authen Config{ authoritative    } = on
     25364 Apache::AuthDBI::authen Config{ data_source      } = dbi:Pg:dbname=test_auth
     25364 Apache::AuthDBI::authen Config{ grp_field        } = groupid
     25364 Apache::AuthDBI::authen Config{ encrypted        } = on
     25364 Apache::AuthDBI::authen Config{ pwd_field        } = passwd
     25364 Apache::AuthDBI::authen Config{ nopasswd         } = off
     25364 Apache::AuthDBI::authen Config{ grp_table        } = groups
     25364 Apache::AuthDBI::authen Config{ pwd_table        } = users
     25364 Apache::AuthDBI::authen Config{ password         } = 
     25364 Apache::AuthDBI::authen Config{ log_string       } = 
     25364 Apache::AuthDBI::authen Config{ uidcasesensitive } = on
     25364 Apache::AuthDBI::authen Config{ username         } = 
     25364 Apache::AuthDBI::authen Config{ grp_whereclause  } = 
     25364 Apache::AuthDBI::authen passwd not found in cache 
     25364 Apache::AuthDBI::authen statement: SELECT passwd FROM users WHERE userid = 'support'
     25364 Apache::AuthDBI::authen passwd = >su7/poGcpDQWY<
     25364 Apache::AuthDBI::authen user support: password match for >su7/poGcpDQWY< 
     25364 Apache::AuthDBI::authen return OK


2.2. normal authentication as above, check if cached password is used

     discard all traces up to the Config section

     25519 Apache::AuthDBI::authen cache: found >support,dbi:Pg:dbname=test_auth,users,userid< >935176023< >su7/poGcpDQWY< 
     25519 Apache::AuthDBI::authen passwd found in cache 
     25519 Apache::AuthDBI::authen passwd = >su7/poGcpDQWY<
     25519 Apache::AuthDBI::authen user support: password match for >su7/poGcpDQWY< 
     25519 Apache::AuthDBI::authen secs since last CleanupHandler: 164, CleanupTime: 60 
     25519 Apache::AuthDBI::authen return OK


2.3. after successful authentication change password in database.
     Restart browser and check if password is looked up in the database again.


2.4. check normal authentication with several users, who share the same userid,
     but who have different passwords.


2.5. check normal authentication with more than one data_source parameter 
     (and corresponding usernames and passwords), where the first connect 
     fails and the second succeeds. Expect to see a warning about the failure. 


-------------------------------------------------------------------


3. Apache::AuthDBI::authz
-------------------------

3.1. normal group authorization, setCacheTime(60)

traces.txt  view on Meta::CPAN

     25560 Apache::AuthDBI::authz  statement: SELECT groupid FROM groups WHERE userid = 'support'
     25560 Apache::AuthDBI::authz  groups = >group-support<
     25560 Apache::AuthDBI::authz  user support: group_result = OK for >group-support< 
     25560 Apache::AuthDBI::authz  return OK
     ==========
     25560 Apache::AuthDBI::authz  request type = >< 
     ==========
     25560 Apache::AuthDBI::authz  request type = >main< 


3.2. normal authorization as above, check if cached password is used

     ==========
     25560 Apache::AuthDBI::authz  request type = >initial main< 
     25560 Apache::AuthDBI::authz  user sent = >support<
     25560 Apache::AuthDBI::authz  requirements: valid-user=>< user=>w3master< group=>group-a group-b group-support group-customer< 
     25560 Apache::AuthDBI::authz  cache: found >support,dbi:Pg:dbname=test_auth,users,userid< >935176510< >group-support< 
     25560 Apache::AuthDBI::authz  groups found in cache 
     25560 Apache::AuthDBI::authz  groups = >group-support<
     25560 Apache::AuthDBI::authz  user support: group_result = OK for >group-support< 
     25560 Apache::AuthDBI::authz  return OK

traces.txt  view on Meta::CPAN

     1682 Apache::AuthDBI::authen push PerlCleanupHandler 
     1682 Apache::AuthDBI::authen return OK
     ==========
     1682 Apache::AuthDBI::authen request type = ><
     ==========
     1682 Apache::AuthDBI::authen request type = >main<
     1682 Apache::AuthDBI         PerlCleanupHandler 
     1682 Apache::AuthDBI         PerlCleanupHandler keep   >support,dbi:Pg:dbname=test_auth,users,userid< 


4.3. Authentication: check if a previously used userid/password is deleted from cache, after 
     the CacheTime has expired. For this, re-start the browser, re-authenticate with another 
     userid and wait, until CacheTime and CleanupTime have expired. 

     1760 Apache::AuthDBI         PerlCleanupHandler delete >w3master,dbi:Pg:dbname=test_auth,users,userid<, last access 157 s before 


4.4. Authorization: check if a previously used userid/group is deleted from cache, after 
     the CacheTime has expired. For this, re-start the browser, re-authenticate with another 
     userid and wait, until CacheTime and CleanupTime have expired.



( run in 0.623 second using v1.01-cache-2.11-cpan-49f99fa48dc )