OK, here we go.
This mod requires that the module CGI.pm is installed on the server. This is highly likely, because CGI.pm is part of the standard perl distribution since perl 5.004.
This mod offers users the possibility to check, on the login form, whether they want to log on automatically next time they call the script. When the box is checked, the script creates a cookie after username and password have been validated.
Every time someone calls the script, it checks whether a session ID is passed to it. If that's the case - fine, the user is already logged on. This is how dbman as such works.
Now here's the change: If there's no session ID passed to the script, the mod checks whether there's a cookie. If so, the cookie is read and a session ID is created based on the username stored in it. This means that the user is logged on automatically (user rights are assigned at a later stage in the script.) If there is no cookie and no session ID, the mod does nothing, and dbman sends the user to the login form.
NOTE: In the code given below, replace the two uses of COOKIENAME by whatever you want to call the cookie (e.g. "dbmankellner"), and replace YOURDOMAIN in the code for auth.pl with the URL of your domain (e.g. "www.yourdomain.com"). The URL must have at least two periods in it to be properly identifiable as a URL. The expiry date is set to three months here. You can change this as you like: "+1h" means one hour from now, "+10d" ten days, "+1M" one month, "+1Y" one year, and so forth.
Here's the actual mod:
1) In html.pl, sub html_login_form, add this hidden input field to the login form:
<input type="checkbox" name="rememberme"> log on automatically next time (requires cookie)
2) Add this line to the beginning of auth.pl:
use CGI qw(:standard);
3) In auth.pl, replace the subroutine auth_check_password with this code:
sub auth_check_password {
# --------------------------------------------------------
# This routine checks to see if the password and userid found
# in %in (must be 'pw' and 'userid') match a valid password and
# userid in the password file.
# It returns a status message and a userid which is built by a
# "user name" + "random number"
# which get's stored in the query string.
my ($pass, @passwd, $userid, $pw, @permissions, $file, $uid);
my ($server_auth) = $ENV{'REMOTE_USER'} || $ENV{'AUTH_USER'};
if ($auth_no_authentication || (($db_uid eq 'default') && $auth_allow_default)) {
return ('ok', 'default', @auth_default_permissions);
}
elsif ($server_auth) { # The user has logged in via server authentication.
return ('ok', $server_auth, &auth_check_permissions($server_auth));
}
elsif ($in{'login'}) { # The user is trying to login.
open (PASSWD, "<$auth_pw_file") || &cgierr("unable to open password file. Reason: $!\n");
@passwds = <PASSWD>; # Let's get the user id and passwords..
close PASSWD;
my ($view, $add, $mod, $del, $admin);
PASS: foreach $pass (@passwds) { # Go through each pass and see if we match..
next PASS if ($pass =~ /^$/); # Skip blank lines.
next PASS if ($pass =~ /^#/); # Skip Comment lines.
chomp ($pass);
($userid, $pw, $view, $add, $del, $mod, $admin) = split (/:/, $pass);
if (($in{'userid'} eq $userid) && (crypt($in{'pw'}, $pw) eq $pw)) {
srand( time() ^ ($$ + ($$ << 15)) ); # Seed Random Number
$db_uid = "$userid." . time() . (int(rand(100000)) + 1);# Build User Id
open(AUTH, ">$auth_dir/$db_uid") or &cgierr("unable to open auth file: $auth_dir/$uid. Reason: $!\n");
print AUTH "$uid: $ENV{'REMOTE_HOST'}\n";
close AUTH;
foreach (0 .. 3) { $permissions[$_] = int($permissions[$_]); }
&auth_logging('logged on', $userid) if ($auth_logging);
## begin cookie mod ############
if ($in{'rememberme'}) {
my $q = new CGI;
my %cookval;
$cookval{'username'} = "$userid";
$cookval{'password'} = crypt($in{'pw'}, $pw);
my $cookie_out = $q->cookie(-name=>"COOKIENAME",
-value=> \%cookval,
-domain => "YOURDOMAIN",
-expires=>'+3M', # cookie expires after three months - adjust as you please
-path=>'/',
);
print $q->header(-cookie=>$cookie_out);
$html_headers_printed = 1;
}
# end cookie mod ##############
return ('ok', $db_uid, $view, $add, $del, $mod, $admin);
}
}
return ("invalid username/password");
}
elsif ($db_uid) { # The user already has a user id given by the program.
(-e "$auth_dir/$db_uid") ?
return ('ok', $db_uid, &auth_check_permissions($db_uid)) :
return ('invalid/expired user session');
}
else { # User has not logged on yet.
return 'no login';
}
}
4) Change the beginning of db.cgi to this code:
#!/usr/bin/perl
use CGI qw(:standard);
$db_script_path = ".";
# Load the form information and set the config file and userid.
local(%in) = &parse_form;
$in{'db'} ? ($db_setup = $in{'db'}) : ($db_setup = 'default');
# Required Librariers
# --------------------------------------------------------
# Make sure we are using perl 5.003, load the config file, and load the auth file.
eval {
unshift (@INC, $db_script_path);
require 5.003; # We need at least Perl 5.003
unless ($db_setup =~ /^[A-Za-z0-9]+$/) { die "Invalid config file name: $db_setup"; }
require "$db_setup.cfg"; # Database Definition File
require "auth.pl"; # Authorization Routines
};
if ($@) { &cgierr ("Error loading required libraries.\nCheck that they exist, permissions are set correctly and that they compile.\nReason: $@"); }
# If we are using benchmarking, then we start a timer and stop it around &main. Then we print the difference.
if ($db_benchmark) { $t0 = new Benchmark; }
################ begin cookie mod
if ($in{'uid'}) { $db_uid = $in{'uid'};} # if the user is logged on already: set $db_uid to the value of parameter "uid"
else { # if user is not logged on: check whether there's a cookie
my $q = new CGI;
my %cookval = $q->cookie("COOKIENAME");
my $userid = $cookval{'username'};
my $password = $cookval{'password'};
if ($userid) { # if there's a cookie, create a session ID using the username from the cookie
srand( time() ^ ($$ + ($$ << 15)) ); # Seed Random Number
$db_uid = "$userid." . time() . (int(rand(100000)) + 1);# Build User Id
open(AUTH, ">$auth_dir/$db_uid") or &cgierr("unable to open auth file: $auth_dir/$db_uid. Reason: $!\n");
print AUTH "$db_uid: $ENV{'REMOTE_HOST'}\n";
close AUTH;
}
else { # if there's no cookie - no session ID. Script will take user to login form later.
$db_uid = "";}
}
######################## end cookie mod
eval { &main; };
One thing: the password is printed to the cookie (encrypted). I'm not sure whether it is necessary to store the password to begin with, as we're not really doing anything with it. It would be possible to authenticate the user again after the cookie is read, just to make sure, but I guess that wouldn't be necessary. For if a deleted user is automatically logged on, the script will then proceed to assign the appropriate rights to them on the basis of the password file. Alas, it won't find the user, and the user doesn't get any rights. At this point the script would return an error, so there's no danger that deleted users still mess with your database because they have a cookie stored on their computer.
Further suggestions for improving the code would be greatly appreciated.
kellner