use strict; use warnings; use English qw(-no_match_vars); use FindBin qw($Bin); use lib "$Bin/mock_libs"; use Crypt::CBC; # from mocks use Digest::MD5 qw( md5_hex ); # from mocks use Data::Dumper; use Test::More tests => 30; my $PACKAGE = 'Apache2::AuthCookieDBI'; my $EMPTY_STRING = q{}; use_ok($PACKAGE); test_defined_or_empty(); test_encrypt_session_key(); test_dir_config_var(); test_authen_ses_key(); test_get_cipher_type(); test__dbi_connect(); exit; sub set_up { my $auth_name = shift; my $mock_config = shift; my $r = Apache2::RequestRec->new( auth_name => $auth_name, mock_config => $mock_config ); # from mock_libs return $r; } sub test_authen_ses_key { my $auth_name = 'testing_authen_ses_key'; my $secret_key = 'test secret key'; my $mock_config = { $auth_name . 'DBI_DSN' => 'test DSN', $auth_name . 'DBI_SecretKey' => $secret_key, $auth_name . 'DBI_User' => $auth_name, $auth_name . 'DBI_Password' => 'test DBI password', $auth_name . 'DBI_UsersTable' => 'users', $auth_name . 'DBI_UserField' => 'user', $auth_name . 'DBI_passwordfield' => 'password', $auth_name . 'DBI_crypttype' => 'none', $auth_name . 'DBI_groupstable' => 'groups', $auth_name . 'DBI_groupfield' => 'grp', $auth_name . 'DBI_groupuserfield' => 'user', $auth_name . 'DBI_encryptiontype' => 'none', $auth_name . 'DBI_sessionlifetime' => '00-24-00-00', $auth_name . 'DBI_sessionmodule' => 'none', }; my $r = set_up( $auth_name, $mock_config ); my $expected_user = 'expected_username'; my $issue_time = '2006-02-04-10-34-23'; my $expire_time = '9999-02-04-10-45-00'; my $session_id = 'test_session_id'; my $extra_session_info = 'extra:info'; my $hashed_string = 'bad-key-stored-in-ticket'; # not a 32 char hex string my $encrypted_session_key = join( q{:}, $expected_user, $issue_time, $expire_time, $session_id, $hashed_string ); Apache2::AuthCookieDBI->authen_ses_key( $r, $encrypted_session_key ); like( $r->log_error->[-1], qr/ bad \s encrypted \s session_key /xm, 'authen_ses_key() on bad encrypted key' ); $r = set_up( $auth_name, $mock_config ); my $seperator = q{:}; my $public_part = join( $seperator, $expected_user, $issue_time, $expire_time, $session_id, $extra_session_info ); my $plaintext_key = join( $seperator, $public_part, $secret_key ); my $md5_hash = md5_hex($plaintext_key); $hashed_string = md5_hex( join( $seperator, $secret_key, $md5_hash ) ); $encrypted_session_key = join( q{:}, $public_part, $hashed_string ); my $got_user = Apache2::AuthCookieDBI->authen_ses_key( $r, $encrypted_session_key ); is( $got_user, $expected_user, 'authen_ses_key() on plaintext key' ) || diag join( "\n", @{ $r->log_error() } ); return 1; } sub test_dir_config_var { my $auth_name = 'testing_dir_config_var'; my $variable_wanted = 'Arbitrary_Variable_Name'; my $config_key = $auth_name . $variable_wanted; my $mock_config = { $config_key => 'Value for this configuration variable.', }; my $r = set_up( $auth_name, $mock_config ); is( Apache2::AuthCookieDBI::_dir_config_var( $r, $variable_wanted ), $mock_config->{$config_key}, '_dir_config_var() passes correct args to $r->dir_config()' ); return 1; } sub test_defined_or_empty { my $user = 'matisse'; my $password; my @other_stuff = qw( a b c ); is( Apache2::AuthCookieDBI::_defined_or_empty( $user, $password, @other_stuff ), 5, '_defined_or_empty returns expected number of items.' ); return 1; } sub test_encrypt_session_key { my $session_key = 'mock_session_key'; my $secret_key = 'mock secret key'; my $auth_name = 'test_encrypt_session_key'; my $expected = { none => $session_key, des => "DES:$secret_key:$session_key", idea => "IDEA:$secret_key:$session_key", blowfish => "Blowfish:$secret_key:$session_key", blowfish_pp => "Blowfish_PP:$secret_key:$session_key", }; foreach my $encryption_type ( sort keys %{$expected} ) { my @args = ( $session_key, $secret_key, $auth_name, $encryption_type ); my $mock_crypt_text = Apache2::AuthCookieDBI::_encrypt_session_key(@args); is( $mock_crypt_text, $expected->{$encryption_type}, "_encrypt_session_key() using $encryption_type" ); } return 1; } sub test_get_cipher_type { # ( $dbi_encryption_type, $auth_name, $secret_key ) my $auth_name = 'Sample Auth Name'; my $secret_key = 'Sample Secret Key String'; my @test_cases = ( { dbi_encryption_type => 'des', expected_cipher_type => 'DES', }, { dbi_encryption_type => 'idea', expected_cipher_type => 'IDEA', }, { dbi_encryption_type => 'blowfish', expected_cipher_type => 'Blowfish', }, { dbi_encryption_type => 'blowfish_pp', expected_cipher_type => 'Blowfish_PP', }, { dbi_encryption_type => 'BLOWFISH_PP', # verify case-insensitive expected_cipher_type => 'Blowfish_PP', }, ); foreach my $case (@test_cases) { my $dbi_encryption_type = $case->{'dbi_encryption_type'}; my $mock_cbc = Apache2::AuthCookieDBI::_get_cipher_type( $dbi_encryption_type, $auth_name, $secret_key, ); Test::More::is( $mock_cbc->{'secret_key'}, $secret_key, "_get_cipher_type() for $dbi_encryption_type - secret_key" ); my $expected_cipher_type = $case->{'expected_cipher_type'}; Test::More::is( $mock_cbc->{'cipher_type'}, $expected_cipher_type, "_get_cipher_type() for $dbi_encryption_type - cipher_type" ); my $second_mock_from_same_args = Apache2::AuthCookieDBI::_get_cipher_type( $dbi_encryption_type, $auth_name, $secret_key, ); Test::More::is( $second_mock_from_same_args, $mock_cbc, "_get_cipher_type($dbi_encryption_type,$auth_name, $secret_key) cached CBC object" ); } my $unsupported_type = 'BunnyRabbits'; eval { Apache2::AuthCookieDBI::_get_cipher_type( $unsupported_type, $auth_name, $secret_key, ); }; Test::More::like( $EVAL_ERROR, qr/Unsupported encryption type: '$unsupported_type'/, '_get_cipher_type() throws exception on unsupported encryption type.' ); return 1; } sub test__dbi_connect { my $auth_name = 'testing__dbi_connect'; my %mock_config = ( "${auth_name}DBI_DSN" => 'test DBI_DSN', "${auth_name}DBI_User" => 'test DBI_User', "${auth_name}DBI_Password" => 'test DBI_Password', "${auth_name}DBI_SecretKey" => 'test DBI_SecretKey', ); my $r = set_up( $auth_name, \%mock_config ); my $mock_dbh = Apache2::AuthCookieDBI::_dbi_connect($r); my $expected = [ $mock_config{"${auth_name}DBI_DSN"}, $mock_config{"${auth_name}DBI_User"}, $mock_config{"${auth_name}DBI_Password"} ]; Test::More::is_deeply( $mock_dbh->{'connect_cached_args'}, $expected, '_dbi_connect() calls connect_cached() with expected arguments.' ) || Test::More::diag( 'Sensor object contains: ', Data::Dumper::Dumper($mock_dbh) ); Test::More::is_deeply( $r->{'_error_messages'}, [], '_dbi_connect() - no unexpected errors.' ); my @expected_errors = ( qq{connect to test DBI_DSN for auth realm $auth_name}, q{_dbi_connect called in main::test__dbi_connect}, ); { no warnings qw(once); local $DBI::CONNECT_CACHED_FORCE_FAIL = 1; Apache2::AuthCookieDBI::_dbi_connect($r); } my @got_errors = @{ $r->{'_error_messages'} }; my $got_failures = 0; for ( my $i = 0; $i <= $#expected_errors; $i++ ) { my $got = $got_errors[$i]; my $expected_regex = qr/$expected_errors[$i]/; Test::More::like( $got, $expected_regex, qq{_dbi_connect() logs error for "$expected_errors[$i]"} ) || $got_failures++; } if ($got_failures) { Test::More::diag( 'Mock request object contains: ', Data::Dumper::Dumper($r) ); } return 1; }