FAQ-OMatic
view release on metacpan or search on metacpan
lib/FAQ/OMatic/Auth.pm view on Meta::CPAN
# Use an existing cookie if available. (why is this good? Just
# to keep the cookies file slimmer?)
my ($cookie,$cid,$ctime);
($cookie,$cid,$ctime) = findCookie($id,'id');
return $cookie if (defined $cookie);
$cookie = "ck".FAQ::OMatic::Entropy::gatherRandomString();
my $cookiesFile = "$FAQ::OMatic::Config::metaDir/cookies";
open COOKIEFILE, ">>$cookiesFile";
print COOKIEFILE "$cookie $id ".time()."\n";
close COOKIEFILE;
if (not chmod(0600, "$cookiesFile")) {
FAQ::OMatic::gripe('problem', "chmod failed on $cookiesFile");
}
return $cookie;
}
sub findCookie {
my $match = shift;
my $by = shift;
my ($cookie,$cid,$ctime);
if (not open COOKIEFILE, "<$FAQ::OMatic::Config::metaDir/cookies") {
return undef;
}
while (defined($_=<COOKIEFILE>)) {
chomp;
($cookie,$cid,$ctime) = split(' ');
# ignore dead cookies
my $cookieActual = FAQ::OMatic::getLocal('cookieActual')
|| $FAQ::OMatic::Config::cookieLife
|| 3600;
next if ((time() - $ctime) > $cookieActual);
if (($by eq 'id') and ($cid eq $match)) {
close COOKIEFILE;
return ($cookie,$cid,$ctime);
}
if (($by eq 'cookie') and ($cookie eq $match)) {
close COOKIEFILE;
return ($cookie,$cid,$ctime);
}
}
close COOKIEFILE;
return undef;
}
# these functions manipulate a file that maps IDs to
# (ID,password,...) tuples. (... = future expansion)
# Right now it's a flat file, but maybe someday it should be a
# dbm file if anyone ever has zillions of authorized posters.
# given an ($id,$password,...) array, writes it into idfile
sub writeIDfile {
my ($id,$password,@rest) = @_;
my $lockf = FAQ::OMatic::lockFile("idfile");
FAQ::OMatic::gripe('error', "idfile is locked.") if (not $lockf);
if (not open(IDFILE, "<$FAQ::OMatic::Config::metaDir/idfile")) {
FAQ::OMatic::unlockFile($lockf);
FAQ::OMatic::gripe('abort', "FAQ::OMatic::Auth::writeIDfile: Couldn't "
."read $FAQ::OMatic::Config::metaDir/idfile because $!");
return;
}
# read id mappings in
my %idmap;
my ($idf,$passf,@restf);
while (defined($_=<IDFILE>)) {
chomp;
($idf,$passf,@restf) = split(' ');
$idmap{$idf} = $_;
}
close IDFILE;
# change the mapping for id
$idmap{$id} = join(' ', $id, $password, @rest);
# write id mappings.
if (not open(IDFILE, ">$FAQ::OMatic::Config::metaDir/idfile-new")) {
FAQ::OMatic::unlockFile($lockf);
FAQ::OMatic::gripe('abort', "FAQ::OMatic::Auth::writeIDfile: Couldn't "
."write $FAQ::OMatic::Config::metaDir/idfile-new because $!");
return;
}
foreach $idf (sort keys %idmap) {
print IDFILE $idmap{$idf}."\n";
}
close IDFILE;
unlink("$FAQ::OMatic::Config::metaDir/idfile") or
FAQ::OMatic::gripe('abort', "FAQ::OMatic::Auth::writeIDfile: Couldn't "
."unlink $FAQ::OMatic::Config::metaDir/idfile because $!");
rename("$FAQ::OMatic::Config::metaDir/idfile-new", "$FAQ::OMatic::Config::metaDir/idfile") or
FAQ::OMatic::gripe('abort', "FAQ::OMatic::Auth::writeIDfile: Couldn't "
."rename $FAQ::OMatic::Config::metaDir/idfile-new to idfile because $!");
chmod 0600, "$FAQ::OMatic::Config::metaDir/idfile" or
FAQ::OMatic::gripe('problem', "FAQ::OMatic::Auth::writeIDfile: Couldn't "
."chmod $FAQ::OMatic::Config::metaDir/idfile because $!");
FAQ::OMatic::unlockFile($lockf);
}
# given an id, returns an array starting ($id,$password,...)
sub readIDfile {
my $id = shift || ''; # key to lookup on
my $dontHideVersion = shift || '';
# keep regular lookups from seeing version number
# record. (smacks of a hack, but this is Perl!)
return undef if (($id eq 'version') and (not $dontHideVersion));
my $lockf = FAQ::OMatic::lockFile("idfile");
FAQ::OMatic::gripe('error', "idfile is locked.") if (not $lockf);
if (not open(IDFILE, "<$FAQ::OMatic::Config::metaDir/idfile")) {
FAQ::OMatic::unlockFile($lockf);
FAQ::OMatic::gripe('abort', "FAQ::OMatic::Auth::readIDfile: Couldn't "
."read $FAQ::OMatic::Config::metaDir/idfile because $!");
return undef;
}
my ($idf,$passf,@restf);
while (defined($_=<IDFILE>)) {
chomp;
($idf,$passf,@restf) = split(' ');
last if ($idf eq $id);
}
close IDFILE;
FAQ::OMatic::unlockFile($lockf);
if (defined($idf) and ($idf eq $id)) {
return ($idf,$passf,@restf);
}
return undef;
}
sub checkCryptPass {
my ($cleartext, $crypted) = @_;
if ($crypted =~ m/md5\((\S+),(\S+)\)/) {
# if this record was encoded with the new md5 encoding, then
# it'll contain a big salt and then the result:
my $salt = $1;
my $cryptedResult = $2;
my $attemptedCrypt = md5_hex($salt, $cleartext);
return ($attemptedCrypt eq $cryptedResult);
} else {
# compatibility mode: use crypt()
# We no longer generate passwords with crypt, but we
# allow checking against crypt()ed passwords to avoid
# annoying users with a password-reset demand.
#my $salt = substr($crypted, 0, 2);
# specific fix from Evan Torrie <torrie@pi.pair.com>: most crypt()s
# don't care of there's excess salt, and those with MD5 crypts use
# more than the first two bytes as salt.
my $salt = $crypted;
return (crypt($cleartext, $salt) eq $crypted);
}
}
sub cryptPass {
my $pass = shift;
my $salt = FAQ::OMatic::Entropy::gatherRandomString();
return "md5(".$salt.",".md5_hex($salt.$pass).")";
}
sub authenticate {
my $params = shift;
my $auth = $params->{'auth'};
# if there's a cookie...
( run in 0.586 second using v1.01-cache-2.11-cpan-39bf76dae61 )