Skip Menu |

This queue is for tickets about the Authen-Simple CPAN distribution.

Report information
The Basics
Id: 26465
Status: open
Priority: 0/
Queue: Authen-Simple

People
Owner: Nobody in particular
Requestors: GIFF [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Controlling cache from Apache config file; hashing data in cache
Two wishlist issues here, but I have a patch for both, so I'll do it as one bug. First, because the cache parameter to objects which inherit from Authen::Simple::Adapter is a cache object, it's impossible to configure it from the Apache config file. One solution is to allow the name of the cache class to be put in the config file, and when the object is constructed an object of that class will be created. The attached patch implements this, with the name "cacheclass". If the class name is followed by whitespace, the rest of the setting is taken as a whitespace-seperated list of arguments for the class's constructor. Second, if the authentication information is sensitive, it is less than ideal to store it in a plaintext cache which is readable by the Web server process. It's straightforward to hash the information before caching it. The patch also adds a "cachehash" option, which is the name of a digester which will be passed to Digest->new. If it is set, username/password pairs will be hashed before they are stored in the cache, and before they are looked for in the cache. For example, here's what I have in my httpd.conf: PerlModule Authen::Simple::MyHTTP PerlSetVar AuthenSimpleMyHTTP_cachehash "SHA-256" PerlSetVar AuthenSimpleMyHTTP_cacheclass "Cache::FastMmap share_file /home/www/data/cache/KxAuth.cache" BTW, the code was very straightforward, extremely easy to modify, and a delight to work with. Thanks!
Subject: authen-simple-adapter-sg.patch
--- Adapter.pm~ 2006-01-13 15:19:55.000000000 -0500 +++ Adapter.pm 2007-04-17 23:21:13.000000000 -0400 @@ -8,10 +8,12 @@ use Authen::Simple::Password qw[]; use Carp qw[]; use Params::Validate qw[]; +use Digest; __PACKAGE__->mk_classdata( _options => { } ); -__PACKAGE__->mk_accessors( qw[ cache callback log ] ); +__PACKAGE__->mk_accessors( qw[ cache cachehash callback log cacheclass ] ); +our $cacheclass_loaded; sub new { my $proto = shift; my $class = ref($proto) || $proto; @@ -32,6 +34,17 @@ $self->$method($value); } + if (!$self->cache && $self->cacheclass) { + my($cacheclass,@cacheargs)=split(' ',$self->cacheclass); + if (!$cacheclass_loaded) { + eval "use $cacheclass;"; + if ($@) { + die "Error including '@{[$self->cacheclass]}': $@"; + } + } + $self->cache($cacheclass->new(@cacheargs)); + } + return $self; } @@ -69,9 +82,19 @@ } } + my $hash_upw; + if ($self->cachehash) { + my $hasher = Digest->new($self->cachehash) + or die "Couldn't get hasher '@{[$self->cachehash]}'"; + $hasher->add("$username:$password"); + $hash_upw = $hasher->b64digest; + } else { + $hash_upw="$username:$password"; + } + if ( $self->cache ) { - $status = $self->cache->get("$username:$password"); + $status = $self->cache->get($hash_upw); if ( defined $status ) { @@ -86,7 +109,7 @@ if ( $self->cache && $status ) { - $self->cache->set( "$username:$password" => $status ); + $self->cache->set( $hash_upw => $status ); $self->log->debug( qq/Caching successful authentication status '$status' for user '$username'./ ) if $self->log; @@ -134,6 +157,16 @@ optional => 1 }; + $options->{cachehash} ||= { + type => Params::Validate::SCALAR, + optional => 1 + }; + + $options->{cacheclass} ||= { + type => Params::Validate::SCALAR, + optional => 1 + }; + $class->_options($options); }
Here is a better patch. It allows more flexibility in calling the constructor, caches cache objects (otherwise if they are created from within Apache, they are re-created every time), and provides documentation.
--- Adapter.pm~ 2006-01-13 15:19:55.000000000 -0500 +++ Adapter.pm 2007-04-18 01:20:55.000000000 -0400 @@ -8,9 +8,10 @@ use Authen::Simple::Password qw[]; use Carp qw[]; use Params::Validate qw[]; +use Digest; __PACKAGE__->mk_classdata( _options => { } ); -__PACKAGE__->mk_accessors( qw[ cache callback log ] ); +__PACKAGE__->mk_accessors( qw[ cache cachehash callback log cacheclass ] ); sub new { my $proto = shift; @@ -25,6 +26,9 @@ return $class->SUPER::new->init($params); } +our %cacheclass_loaded; +our %cache_cache; + sub init { my ( $self, $params ) = @_; @@ -32,6 +36,33 @@ $self->$method($value); } + if (!$self->cache && $self->cacheclass) { + if ($cache_cache{$self->cacheclass}) { + $self->cache($cache_cache{$self->cacheclass}); + } else { + my($class,$args)=split(' ',$self->cacheclass,2); + if (!$cacheclass_loaded{$class}) { + eval "use $class;"; + if ($@) { + die "Error including '@{[$self->cacheclass]}': $@"; + } + } + if ($args =~ /^\s*(\w*)\s*\((.*)\)\s*$/) + { + if ($1 eq '') { $args = "new$args"; } + my $dothis = "\$self->cache($class->$args);"; + eval $dothis; + if ($@) { + die "Error executing '$dothis': $@"; + } + } + else + { + $self->cache($class->new(split(' ',$args))); + } + $cache_cache{$self->cacheclass}=$self->cache; + } + } return $self; } @@ -69,9 +100,19 @@ } } + my $hash_upw; + if ($self->cachehash) { + my $hasher = Digest->new($self->cachehash) + or die "Couldn't get hasher '@{[$self->cachehash]}'"; + $hasher->add("$username:$password"); + $hash_upw = $hasher->b64digest; + } else { + $hash_upw="$username:$password"; + } + if ( $self->cache ) { - $status = $self->cache->get("$username:$password"); + $status = $self->cache->get($hash_upw); if ( defined $status ) { @@ -86,7 +127,7 @@ if ( $self->cache && $status ) { - $self->cache->set( "$username:$password" => $status ); + $self->cache->set( $hash_upw => $status ); $self->log->debug( qq/Caching successful authentication status '$status' for user '$username'./ ) if $self->log; @@ -134,6 +175,16 @@ optional => 1 }; + $options->{cachehash} ||= { + type => Params::Validate::SCALAR, + optional => 1 + }; + + $options->{cacheclass} ||= { + type => Params::Validate::SCALAR, + optional => 1 + }; + $class->_options($options); } @@ -203,6 +254,34 @@ cache => Cache::FastMmap->new +=item * cacheclass ( $ ) + +String describing the class that should be used to construct a cache. + +If the class name is alone, the object will be created by calling its +constructor C<new>. If it is followed by a space but no opening +parentheses, the remainder of the string will be treated as a +space-seperated list of arguments to be passed to the constructor +C<new>. If it is followed by a space, an optional constructor name, +then a parenthesized string, the object will be created by calling the +named constructor, or C<new> if no constructor is named, and +processing the remainder of the string as arguments to the constructor +with C<eval>. + +Mostly all of this allows you to create an agent from your httpd.conf +file. All of these will create a Cache::FastMmap cache: + + PerlSetVar AuthenSimpleMyHTTP_cacheclass "Cache::FastMmap new(share_file => '/tmp/cache')" + PerlSetVar AuthenSimpleMyHTTP_cacheclass "Cache::FastMmap (share_file => '/tmp/cache')" + PerlSetVar AuthenSimpleMyHTTP_cacheclass "Cache::FastMmap share_file /tmp/cache" + +=item * cachehash ( $ ) + +The name of a class that will be used to create a one-way hash of the +username and password, to avoid storing them in the cache in +plaintext. The class name will be passed to L<Digest|Digest>->new to +create a new digester object. + =item * callback ( \& ) A subref that gets called with two scalar references, username and password.