Authen-Htpasswd-0.171/0000755000372100001440000000000011620221571014312 5ustar matthewtusersAuthen-Htpasswd-0.171/lib/0000755000372100001440000000000011620221571015060 5ustar matthewtusersAuthen-Htpasswd-0.171/lib/Authen/0000755000372100001440000000000011620221571016304 5ustar matthewtusersAuthen-Htpasswd-0.171/lib/Authen/Htpasswd.pm0000644000372100001440000002140411620221437020441 0ustar matthewtuserspackage Authen::Htpasswd; use 5.005; use strict; use base 'Class::Accessor::Fast'; use Carp; use IO::File; use IO::LockedFile; use Authen::Htpasswd::User; use Scalar::Util qw(blessed); use vars qw{$VERSION $SUFFIX}; $VERSION = '0.171'; $VERSION = eval $VERSION; $SUFFIX = '.new'; __PACKAGE__->mk_accessors(qw/ file encrypt_hash check_hashes /); =head1 NAME Authen::Htpasswd - interface to read and modify Apache .htpasswd files =head1 SYNOPSIS my $pwfile = Authen::Htpasswd->new('user.txt', { encrypt_hash => 'md5' }); # authenticate a user (checks all hash methods by default) if ($pwfile->check_user_password('bob', 'foo')) { ... } # modify the file (writes immediately) $pwfile->update_user('bob', $password, $info); $pwfile->add_user('jim', $password); $pwfile->delete_user('jim'); # get user objects tied to a file my $user = $pwfile->lookup_user('bob'); if ($user->check_password('vroom', [qw/ md5 sha1 /])) { ... } # only use secure hashes $user->password('foo'); # writes to file $user->set(password => 'bar', extra_info => 'editor'); # change more than one thing at once # or manage the file yourself my $user = Authen::Htpasswd::User->new('bill', { hashed_password => 'iQ.IuWbUIhlPE' }); my $user = Authen::Htpasswd::User->new('bill', 'bar', 'staff', { encrypt_hash => 'crypt' }); print PASSWD $user->to_line, "\n"; =head1 DESCRIPTION This module provides a convenient, object-oriented interface to Apache-style F<.htpasswd> files. It supports passwords encrypted via MD5, SHA1, and crypt, as well as plain (cleartext) passwords. Additional fields after username and password, if present, are accessible via the C array. =head1 METHODS =head2 new my $pwfile = Authen::Htpasswd->new($filename, \%options); Creates an object for a given F<.htpasswd> file. Options: =over 4 =item encrypt_hash How passwords should be encrypted if a user is added or changed. Valid values are C, C, C, and C. Default is C. =item check_hashes An array of hash methods to try when checking a password. The methods will be tried in the order given. Default is C, C, C, C. =back =cut sub new { my $class = shift; my $self = ref $_[-1] eq 'HASH' ? pop @_ : {}; $self->{file} = $_[0] if $_[0]; croak "no file specified" unless $self->{file}; if (!-e $self->{file}) { open my $file, '>', $self->{file} or die $!; close $file or die $!; } $self->{encrypt_hash} ||= 'crypt'; $self->{check_hashes} ||= [ Authen::Htpasswd::Util::supported_hashes() ]; unless ( defined $self->{write_locking} ) { if ( $^O eq 'MSWin32' or $^O eq 'cygwin' ) { $self->{write_locking} = 0; } else { $self->{write_locking} = 1; } } bless $self, $class; } =head2 lookup_user my $userobj = $pwfile->lookup_user($username); Returns an L object for the given user in the password file. =cut sub lookup_user { my ($self,$search_username) = @_; my $file = IO::LockedFile->new($self->file, 'r') or die $!; while (defined(my $line = <$file>)) { chomp $line; my ($username,$hashed_password,@extra_info) = split /:/, $line; if ($username eq $search_username) { $file->close or die $!; return Authen::Htpasswd::User->new($username,undef,@extra_info, { file => $self, hashed_password => $hashed_password, encrypt_hash => $self->encrypt_hash, check_hashes => $self->check_hashes }); } } $file->close or die $!; return undef; } =head2 all_users my @users = $pwfile->all_users; =cut sub all_users { my $self = shift; my @users; my $file = IO::LockedFile->new($self->file, 'r') or die $!; while (defined(my $line = <$file>)) { chomp $line; my ($username,$hashed_password,@extra_info) = split /:/, $line; push(@users, Authen::Htpasswd::User->new($username,undef,@extra_info, { file => $self, hashed_password => $hashed_password, encrypt_hash => $self->encrypt_hash, check_hashes => $self->check_hashes })); } $file->close or die $!; return @users; } =head2 check_user_password $pwfile->check_user_password($username,$password); Returns whether the password is valid. Shortcut for C<< $pwfile->lookup_user($username)->check_password($password) >>. =cut sub check_user_password { my ($self,$username,$password) = @_; my $user = $self->lookup_user($username); croak "could not find user $username" unless $user; return $user->check_password($password); } =head2 update_user $pwfile->update_user($userobj); $pwfile->update_user($username, $password[, @extra_info], \%options); Modifies the entry for a user saves it to the file. If the user entry does not exist, it is created. The options in the second form are passed to L. =cut sub update_user { my $self = shift; my $user = $self->_get_user(@_); my $username = $user->username; my ($old,$new) = $self->_start_rewrite; my $seen = 0; while (defined(my $line = <$old>)) { if ($line =~ /^\Q$username\E:/) { chomp $line; my (undef,undef,@extra_info) = split /:/, $line; $user->{extra_info} ||= [ @extra_info ] if scalar @extra_info; $self->_print( $new, $user->to_line . "\n" ); $seen++; } else { $self->_print( $new, $line ); } } $self->_print( $new, $user->to_line . "\n" ) unless $seen; $self->_finish_rewrite($old,$new); } =head2 add_user $pwfile->add_user($userobj); $pwfile->add_user($username, $password[, @extra_info], \%options); Adds a user entry to the file. If the user entry already exists, an exception is raised. The options in the second form are passed to L. =cut sub add_user { my $self = shift; my $user = $self->_get_user(@_); my $username = $user->username; my ($old,$new) = $self->_start_rewrite; while (defined(my $line = <$old>)) { if ($line =~ /^\Q$username\E:/) { $self->_abort_rewrite($old,$new); croak "user $username already exists in " . $self->file . "!"; } $self->_print( $new, $line ); } $self->_print( $new, $user->to_line . "\n" ); $self->_finish_rewrite($old,$new); } =head2 delete_user $pwfile->delete_user($userobj); $pwfile->delete_user($username); Removes a user entry from the file. =cut sub delete_user { my $self = shift; my $username = blessed($_[0]) && $_[0]->isa('Authen::Htpasswd::User') ? $_[0]->username : $_[0]; my ($old,$new) = $self->_start_rewrite; while (defined(my $line = <$old>)) { next if $line =~ /^\Q$username\E:/; $self->_print( $new, $line ); } $self->_finish_rewrite($old,$new); } sub _print { my ($self,$new,$string) = @_; if ( $self->{write_locking} ) { print $new $string; } else { $$new .= $string; } } sub _get_user { my $self = shift; return $_[0] if blessed($_[0]) && $_[0]->isa('Authen::Htpasswd::User'); my $attr = ref $_[-1] eq 'HASH' ? pop @_ : {}; $attr->{encrypt_hash} ||= $self->encrypt_hash; $attr->{check_hashes} ||= $self->check_hashes; return Authen::Htpasswd::User->new(@_, $attr); } sub _start_rewrite { my $self = shift; if ( $self->{write_locking} ) { my $old = IO::LockedFile->new($self->file, 'r+') or die $!; my $new = IO::File->new($self->file . $SUFFIX, 'w') or die $!; return ($old,$new); } else { my $old = IO::File->new( $self->file, 'r' ) or die $!; my $new = ""; return ($old, \$new); } } sub _finish_rewrite { my ($self,$old,$new) = @_; if ( $self->{write_locking} ) { $new->close or die $!; rename $self->file . $SUFFIX, $self->file or die $!; $old->close or die $!; } else { $old->close or die $!; $old = IO::File->new( $self->file, 'w' ) or die $!; print $old $$new; $old->close or die $!; } } sub _abort_rewrite { my ($self,$old,$new) = @_; if ( $self->{write_locking} ) { $new->close; $old->close; unlink $self->file . $SUFFIX; } else { $old->close; } } =head1 AUTHOR David Kamholz C Yuval Kogman =head1 SEE ALSO L. =head1 COPYRIGHT & LICENSE Copyright (c) 2005 - 2007 the aforementioned authors. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; Authen-Htpasswd-0.171/lib/Authen/Htpasswd/0000755000372100001440000000000011620221571020101 5ustar matthewtusersAuthen-Htpasswd-0.171/lib/Authen/Htpasswd/User.pm0000644000372100001440000001177711620211575021375 0ustar matthewtuserspackage Authen::Htpasswd::User; use strict; use base 'Class::Accessor::Fast'; use Carp; use Authen::Htpasswd; use Authen::Htpasswd::Util; use overload '""' => \&to_line, bool => sub { 1 }, fallback => 1; __PACKAGE__->mk_accessors(qw/ file encrypt_hash check_hashes /); =head1 NAME Authen::Htpasswd::User - represents a user line in a .htpasswd file =head1 SYNOPSIS my $user = Authen::Htpasswd::User->new($username, $password[, @extra_info], \%options); my $user = $pwfile->lookup_user($username); # from Authen::Htpasswd object if ($user->check_password($password)) { ... } if ($user->hashed_password eq $foo) { ... } # these are written immediately if the user was looked up from an Authen::Htpasswd object $user->username('bill'); $user->password('bar'); $user->hashed_password('tIYAwma5mxexA'); $user->extra_info('root', 'joe@site.com', 'Joe Sysadmin'); $user->set(username => 'bill', password => 'foo'); # set several at once print $user->to_line, "\n"; =head1 METHODS =head2 new my $userobj = Authen::Htpasswd::User->new($username, $password[, @extra_info], \%options); Creates a user object. You may also specify the arguments and options together in a hash: C<< { username => $foo, password => $bar, extra_info => [$email, $name], ... } >>. =over 4 =item encrypt_hash =item check_hashes See L. =item hashed_password Explicitly sets the value of the hashed password, rather than generating it with C. =back =cut sub new { my $class = shift; croak "not enough arguments" if @_ < 2; my $self = ref $_[-1] eq 'HASH' ? pop @_ : {}; $self->{encrypt_hash} ||= 'crypt'; $self->{check_hashes} ||= [ Authen::Htpasswd::Util::supported_hashes() ]; $self->{autocommit} = 1; $self->{username} = $_[0]; $self->{hashed_password} ||= htpasswd_encrypt($self->{encrypt_hash}, $_[1]) if defined $_[1]; $self->{extra_info} = [ @_[2..$#_] ] if defined $_[2]; bless $self, $class; } =head2 check_password $userobj->check_password($password,\@check_hashes); Returns whether the password matches. C is the same as for Authen::Htpasswd. =cut sub check_password { my ($self,$password,$hashes) = @_; $hashes ||= $self->check_hashes; foreach my $hash (@$hashes) { return 1 if $self->hashed_password eq htpasswd_encrypt($hash, $password, $self->hashed_password); } return 0; } =head2 username =head2 hashed_password =head2 extra_info(@fields) Get and set the fields of the user line. These methods, as well as C and C below, write any changes immediately if the user was lookup up from an Authen::Htpasswd object. If the username is changed, the old entry is I preserved. =cut sub username { my $self = shift; if (@_) { $self->{old_username} = $self->{username} if $self->{username} ne $_[0]; $self->{username} = shift; $self->_update if $self->{autocommit}; } return $self->{username}; } sub hashed_password { my $self = shift; if (@_) { $self->{hashed_password} = shift; $self->_update if $self->{autocommit}; } return $self->{hashed_password}; } sub extra_info { my $self = shift; if (@_) { $self->{extra_info} = [ @_ ]; $self->_update if $self->{autocommit}; } return $self->{extra_info}; } =head2 password $userobj->password($newpass); Encrypts a new password. Dies if C<$newpass> is not provided. =cut sub password { my ($self,$password) = @_; croak "you must provide a new password" unless defined $password; $self->hashed_password( htpasswd_encrypt($self->encrypt_hash, $password) ); } =head2 set $userobj->set(item => $value, ...); Sets any of the four preceding values at once. Only writes the file once if it is going to be written. =cut sub set { my ($self,%attr) = @_; $self->{autocommit} = 0; while (my ($key,$value) = each %attr) { croak "don't know how to set $key" unless $self->can($key); $self->$key(ref $value eq 'ARRAY' ? @$value : $value); } $self->_update; $self->{autocommit} = 1; } =head2 to_line $userobj->to_line; Returns a line for the user, suitable for printing to a C<.htpasswd> file. There is no newline at the end. =cut sub to_line { my $self = shift; return join(':', $self->username, $self->hashed_password, defined $self->extra_info ? @{$self->extra_info} : ()); } sub _update { my $self = shift; if ($self->file) { if (defined $self->{old_username}) { $self->file->delete_user($self->{old_username}); delete $self->{old_username}; } $self->file->update_user($self); } } =head1 AUTHOR David Kamholz C Yuval Kogman =head1 COPYRIGHT & LICENSE Copyright (c) 2005 - 2007 the aforementioned authors. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; Authen-Htpasswd-0.171/lib/Authen/Htpasswd/Util.pm0000644000372100001440000000445311620211575021365 0ustar matthewtuserspackage Authen::Htpasswd::Util; use strict; use Digest; use Carp; use vars qw{@ISA @EXPORT}; BEGIN { require Exporter; @ISA = qw/ Exporter /; @EXPORT = qw/ htpasswd_encrypt /; } my @CRYPT_CHARS = split(//, './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'); =head1 NAME Authen::Htpasswd::Util - performs encryption of supported .htpasswd formats =head1 METHODS =head2 htpasswd_encrypt htpasswd_encrypt($hash,$password,$hashed_password); Encrypts a cleartext $password given the specified $hash (valid values are C, C, C, or C). For C and C it is sometimes necessary to pass the old encrypted password as $hashed_password to be sure that the new one uses the correct salt. Exported by default. =cut sub htpasswd_encrypt { my ($hash,$password,$hashed_password) = @_; my $meth = __PACKAGE__->can("_hash_$hash"); croak "don't know how to handle $hash hash" unless $meth; return &$meth($password,$hashed_password); } =head2 supported_hashes my @hashes = Authen::Htpasswd::Util::supported_hashes(); Returns an array of hash types available. C and C are always available. C is checked by attempting to load it via L. C requires L. =cut sub supported_hashes { my @supported = qw/ crypt plain /; eval { Digest->new("SHA-1") }; unshift @supported, 'sha1' unless $@; eval { require Crypt::PasswdMD5 }; unshift @supported, 'md5' unless $@; return @supported; } sub _hash_plain { my ($password) = @_; return $password; } sub _hash_crypt { my ($password,$salt) = @_; $salt = join('', @CRYPT_CHARS[int rand 64, int rand 64]) unless $salt; return crypt($password,$salt); } sub _hash_md5 { my ($password,$salt) = @_; require Crypt::PasswdMD5; return Crypt::PasswdMD5::apache_md5_crypt($password,$salt); } sub _hash_sha1 { my ($password) = @_; my $sha1 = Digest->new("SHA-1"); $sha1->add($password); return '{SHA}' . $sha1->b64digest . '='; } =head1 AUTHOR David Kamholz C Yuval Kogman =head1 COPYRIGHT & LICENSE Copyright (c) 2005 - 2007 the aforementioned authors. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; Authen-Htpasswd-0.171/README0000644000372100001440000000670511620211575015205 0ustar matthewtusersNAME Authen::Htpasswd - interface to read and modify Apache .htpasswd files SYNOPSIS my $pwfile = Authen::Htpasswd->new('user.txt', { encrypt_hash => 'md5' }); # authenticate a user (checks all hash methods by default) if ($pwfile->check_user_password('bob', 'foo')) { ... } # modify the file (writes immediately) $pwfile->update_user('bob', $password, $info); $pwfile->add_user('jim', $password); $pwfile->delete_user('jim'); # get user objects tied to a file my $user = $pwfile->lookup_user('bob'); if ($user->check_password('vroom', [qw/ md5 sha1 /])) { ... } # only use secure hashes $user->password('foo'); # writes to file $user->set(password => 'bar', extra_info => 'editor'); # change more than one thing at once # or manage the file yourself my $user = Authen::Htpasswd::User->new('bill', { hashed_password => 'iQ.IuWbUIhlPE' }); my $user = Authen::Htpasswd::User->new('bill', 'bar', 'staff', { encrypt_hash => 'crypt' }); print PASSWD $user->to_line, "\n"; DESCRIPTION This module provides a convenient, object-oriented interface to Apache-style .htpasswd files. It supports passwords encrypted via MD5, SHA1, and crypt, as well as plain (cleartext) passwords. It requires Crypt::PasswdMD5 for MD5 and Digest::SHA1 for SHA1. Additional fields after username and password, if present, are accessible via the "extra_info" array. METHODS new my $pwfile = Authen::Htpasswd->new($filename, \%options); Creates an object for a given .htpasswd file. Options: encrypt_hash How passwords should be encrypted if a user is added or changed. Valid values are "md5", "sha1", "crypt", and "plain". Default is "crypt". check_hashes An array of hash methods to try when checking a password. The methods will be tried in the order given. Default is "md5", "sha1", "crypt", "plain". lookup_user my $userobj = $pwfile->lookup_user($username); Returns an Authen::Htpasswd::User object for the given user in the password file. all_users my @users = $pwfile->all_users; check_user_password $pwfile->check_user_password($username,$password); Returns whether the password is valid. Shortcut for "$pwfile->lookup_user($username)->check_password($password)". update_user $pwfile->update_user($userobj); $pwfile->update_user($username, $password[, @extra_info], \%options); Modifies the entry for a user saves it to the file. If the user entry does not exist, it is created. The options in the second form are passed to Authen::Htpasswd::User. add_user $pwfile->add_user($userobj); $pwfile->add_user($username, $password[, @extra_info], \%options); Adds a user entry to the file. If the user entry already exists, an exception is raised. The options in the second form are passed to Authen::Htpasswd::User. delete_user $pwfile->delete_user($userobj); $pwfile->delete_user($username); Removes a user entry from the file. AUTHOR David Kamholz "dkamholz@cpan.org" Yuval Kogman SEE ALSO Apache::Htpasswd. COPYRIGHT & LICENSE Copyright (c) 2005 the aforementioned authors. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Authen-Htpasswd-0.171/t/0000755000372100001440000000000011620221571014555 5ustar matthewtusersAuthen-Htpasswd-0.171/t/data/0000755000372100001440000000000011620221571015466 5ustar matthewtusersAuthen-Htpasswd-0.171/t/data/passwd.txt0000644000372100001440000000020611620211575017531 0ustar matthewtusersbob:tIYAwma5mxexA:admin:bob@universe.org bill:$apr1$Zg9Z8/..$npqzK0gFp6HgU8OxUhUnr/ fred:{SHA}h6AWXy9FexW0z5c86amnaGvZkhE= joe:secret Authen-Htpasswd-0.171/t/02pod.t0000644000372100001440000000036711620211575015677 0ustar matthewtusers#!perl use strict; BEGIN { $| = 1; $^W = 1; } use Test::More; eval "use Test::Pod 1.14"; plan skip_all => 'Test::Pod 1.14 required' if $@; plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; all_pod_files_ok(); Authen-Htpasswd-0.171/t/05edit.t0000644000372100001440000000467311620213020016034 0ustar matthewtusers#!/perl use strict; BEGIN { $| = 1; $^W = 1; } use Test::More tests => 28; use Authen::Htpasswd; use File::Spec::Functions; use File::Copy; copy(catfile(qw/t data passwd.txt/), catfile(qw/t data temp.txt/)) or die $!; my $file = Authen::Htpasswd->new(catfile(qw/t data temp.txt/)); ok( $file, 'object created successfully'); # we need to have a user with a name that isn't a valid perl package to # avoid hiding a bug where we call $_[0]->isa on the username. ok( $file->add_user(qw/ 3jim frobnicate /), 'new user created' ); ok( $file->check_user_password(qw/ 3jim frobnicate /), 'new user verified' ); ok( $file->update_user(qw/ fred frobble /), 'user updated' ); ok( $file->check_user_password(qw/ fred frobble /), 'updated user verified' ); ok( !$file->check_user_password(qw/ fred fribble /), 'old password invalid' ); ok( $file->delete_user('3jim'), 'deleted user' ); eval { $file->check_user_password(qw/ 3jim frobnicate /) }; ok( $@, 'deleted user not found' ); my $user = $file->lookup_user('bob'); ok( $user, 'looked up user' ); ok( $user->check_password('margle'), 'verified password' ); ok( $user->password('farble'), 'changed password'); $user = $file->lookup_user('bob'); ok( $user->check_password('farble'), 'verified changed password'); is(scalar @{$user->extra_info}, 2, 'extra info has two fields'); is( $user->extra_info->[0], 'admin', 'verified first field of extra info' ); is( $user->extra_info->[1], 'bob@universe.org', 'verified second field extra info' ); is(scalar @{$user->extra_info}, 2, 'extra info still has two fields'); is( $user->extra_info->[0], 'admin', 'info not clobbered'); ok( $user->extra_info('janitor'), 'changed extra info'); $user = $file->lookup_user('bob'); is(scalar @{$user->extra_info}, 1, 'extra info reduced to one field'); is( $user->extra_info->[0], 'janitor', 'verified extra info'); ok( $user->set( username => 'fred', password => 'orange', extra_info => [qw/ gray black /] ), 'set multiple fields' ); ok( !$file->lookup_user('bob'), 'old name of user not found' ); $user = $file->lookup_user('fred'); ok( $user, 'new name of user found'); is( $user->username, 'fred', 'user has correct username'); ok( $user->check_password('orange'), 'user has correct password'); is(scalar @{$user->extra_info}, 2, 'extra info has two fields'); eval { $user->password() }; ok($@, 'password with no args dies'); ok($user->check_password('orange'), 'user still has correct password'); unlink catfile(qw/t data temp.txt/); Authen-Htpasswd-0.171/t/01use.t0000644000372100001440000000016011620211575015677 0ustar matthewtusers#!perl use strict; BEGIN { $| = 1; $^W = 1; } use Test::More 'no_plan'; use_ok('Authen::Htpasswd'); Authen-Htpasswd-0.171/t/03podcoverage.t0000644000372100001440000000041611620211575017407 0ustar matthewtusers#!perl use strict; BEGIN { $| = 1; $^W = 1; } use Test::More; eval "use Test::Pod::Coverage 1.04"; plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@; plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; all_pod_coverage_ok(); Authen-Htpasswd-0.171/t/04core.t0000644000372100001440000000300011620211575016032 0ustar matthewtusers#!/perl use strict; BEGIN { $| = 1; $^W = 1; } use Test::More tests => 13; use Authen::Htpasswd; use File::Spec::Functions; my $file = Authen::Htpasswd->new(catfile(qw/t data passwd.txt/)); ok( $file, 'object created successfully'); ok( $file->check_user_password(qw/ joe secret /), 'plaintext password verified' ); ok( !$file->check_user_password(qw/ joe tersec /), 'incorrect plaintext password rejected' ); ok( $file->check_user_password(qw/ bob margle /), 'crypt password verified' ); ok( !$file->check_user_password(qw/ bob foogle /), 'incorrect crypt password rejected' ); SKIP: { skip "Crypt::PasswdMD5 is required for md5 passwords", 2 unless grep { $_ eq 'md5' } @{$file->check_hashes}; ok( $file->check_user_password(qw/ bill blargle /), 'md5 password verified' ); ok( !$file->check_user_password(qw/ bill fnord /), 'incorrect md5 password rejected' ); } SKIP: { skip "Digest::SHA1 is required for md5 passwords", 2 unless grep { $_ eq 'sha1' } @{$file->check_hashes}; ok( $file->check_user_password(qw/ fred fribble /), 'sha1 password verified' ); ok( !$file->check_user_password(qw/ fred frobble /), 'incorrect sha1 password rejected' ); } $file->check_hashes([qw/ crypt /]); ok( !$file->check_user_password(qw/ joe secret /), 'correct plaintext password denied'); my @users = $file->all_users; is( scalar @users, 4, 'returned correct number of users' ); is( $users[0]->username, 'bob', 'first user has right name' ); is( $users[-1]->username, 'joe', 'last user has right name' );Authen-Htpasswd-0.171/META.yml0000644000372100001440000000115111620221571015561 0ustar matthewtusers--- abstract: 'Interface to read and modify Apache .htpasswd files' author: - 'David Kamholz ' build_requires: ExtUtils::MakeMaker: 0 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 6.59, CPAN::Meta::Converter version 2.112150' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Authen-Htpasswd no_index: directory: - t - inc requires: Class::Accessor::Fast: 0 Crypt::PasswdMD5: 0 Digest: 0 Digest::SHA1: 0 IO::LockedFile: 0 Scalar::Util: 0 version: 0.171 Authen-Htpasswd-0.171/META.json0000644000372100001440000000205511620221571015735 0ustar matthewtusers{ "abstract" : "Interface to read and modify Apache .htpasswd files", "author" : [ "David Kamholz " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 6.59, CPAN::Meta::Converter version 2.112150", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Authen-Htpasswd", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : 0 } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : 0 } }, "runtime" : { "requires" : { "Class::Accessor::Fast" : 0, "Crypt::PasswdMD5" : 0, "Digest" : 0, "Digest::SHA1" : 0, "IO::LockedFile" : 0, "Scalar::Util" : 0 } } }, "release_status" : "stable", "version" : "0.171" } Authen-Htpasswd-0.171/MANIFEST0000644000372100001440000000057611620221571015453 0ustar matthewtusersChanges lib/Authen/Htpasswd.pm lib/Authen/Htpasswd/User.pm lib/Authen/Htpasswd/Util.pm Makefile.PL MANIFEST This list of files README t/01use.t t/02pod.t t/03podcoverage.t t/04core.t t/05edit.t t/data/passwd.txt META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Authen-Htpasswd-0.171/Makefile.PL0000644000372100001440000000117611620212701016264 0ustar matthewtusersuse strict; use ExtUtils::MakeMaker; require 5.005; WriteMakefile( 'NAME' => 'Authen::Htpasswd', 'ABSTRACT' => 'Interface to read and modify Apache .htpasswd files', 'AUTHOR' => 'David Kamholz ', 'INSTALLDIRS' => 'site', 'LICENSE' => 'perl', 'VERSION_FROM' => 'lib/Authen/Htpasswd.pm', 'PREREQ_PM' => { 'Class::Accessor::Fast' => 0, 'IO::LockedFile' => 0, 'Digest' => 0, 'Digest::SHA1' => 0, 'Crypt::PasswdMD5' => 0, 'Scalar::Util' => 0, }, ); Authen-Htpasswd-0.171/Changes0000644000372100001440000000275411620221567015622 0ustar matthewtusers0.171 Tue Aug 09 13:09:00 BST 2011 - re-disting because I failed to notice MYMETA.* in the dist 0.170 Tue Aug 09 12:17:00 BST 2011 - fix user inflation code to handle arbitrary usernames 0.161 Sun Oct 12 12:13:27 PDT 2008 - fix stupid bug when trying to add a user that already exists (RT #37785) - fix \Q in regular expressions for newer versions of perl (RT #27012) 0.16 Sun Jul 15 15:20:28 PDT 2007 - Disable write locking on Win32. (On Win32 you cannot delete a file with an open filehandle) - use File::Spec in tests to avoid build failure on Windows - add Digest::SHA1 and Crypt::PasswdMD5 as dependencies rather than recommends (still works without them if you don't need SHA or MD5 support) 0.15 Sun Dec 31 02:51:35 EST 2006 - prevent User::password() from clobbering the password if none is passed - document Util::supported_hashes() 0.14 Fri Mar 03 08:01:32 CET 2006 - add all_users method 0.13 Sat Nov 26 04:18:19 CET 2005 - works if you don't have Crypt::PasswdMD5 or Digest::SHA1 - auto-detects available modules for default check_hashes - added Yuval Kogman as author, also added license to POD 0.12 November 10 2005 - extra_info is now an array, as suggested by Uwe Voelker - changing the username will now delete the old username and add the new one 0.11 November 09 2005 - implement locking with IO::LockedFile - minor code and pod cleanups 0.10 November 09 2005 - initial release