view release on metacpan or search on metacpan
[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>]
- 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.
- 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.
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):
- 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,
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:
---------------
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
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
-------------------------------------------------------------------
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)
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
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.