20-Auth-and-password-DBI-dynamic-hash.t 6.04 KB
Newer Older
1 2 3 4
use Lemonldap::NG::Portal::Lib::DBI;
use MIME::Base64;

{
Xavier Guimard's avatar
Xavier Guimard committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    no warnings 'redefine';

    sub Lemonldap::NG::Portal::Lib::DBI::hash_password_from_database {

        # Remark: database function must get hexadecimal input
        # and send back hexadecimal output
        my $self     = shift;
        my $dbh      = shift;
        my $dbmethod = shift;
        my $dbsalt   = shift;
        my $password = shift;

        # Create functions
        use Digest::SHA;
        $dbh->sqlite_create_function(
            'sha256', 1,
            sub {
                my $p = shift;
                return
                  unpack( 'H*',
                    Digest::SHA->new(256)->add( pack( 'H*', $p ) )->digest );
            }
        );
        $dbh->sqlite_create_function(
            'sha512', 1,
            sub {
                my $p = shift;
                return
                  unpack( 'H*',
                    Digest::SHA->new(512)->add( pack( 'H*', $p ) )->digest );
            }
        );

        # convert password to hexa
        my $passwordh = unpack "H*", $password;

        my @rows = ();
        eval {
            my $sth = $dbh->prepare("SELECT $dbmethod('$passwordh$dbsalt')");
            $sth->execute();
            @rows = $sth->fetchrow_array();
        };
        if ($@) {
            $self->logger->error(
                "DBI error while hashing with '$dbmethod' hash function: $@");
            $self->userLogger->warn("Unable to check password");
            return "";
        }

        if ( @rows == 1 ) {
            $self->logger->debug(
"Successfully hashed password with $dbmethod hash function in database"
            );
58

Xavier Guimard's avatar
Xavier Guimard committed
59 60
            # convert salt to binary
            my $dbsaltb = pack 'H*', $dbsalt;
61

Xavier Guimard's avatar
Xavier Guimard committed
62 63
            # convert result to binary
            my $res = pack 'H*', $rows[0];
64

Xavier Guimard's avatar
Xavier Guimard committed
65 66 67 68 69 70 71 72 73 74 75
            return encode_base64( $res . $dbsaltb, '' );
        }
        else {
            $self->userLogger->warn(
                "Unable to check password with '$dbmethod'");
            return "";
        }

        # Return encode_base64(SQL_METHOD(password + salt) + salt)
    }
}
76 77 78 79 80 81 82 83

use Test::More;
use strict;
use IO::String;

require 't/test-lib.pm';

my $res;
Xavier Guimard's avatar
Xavier Guimard committed
84
my $maintests = 6;
85 86 87 88

eval { unlink 't/userdb.db' };

SKIP: {
Xavier Guimard's avatar
Xavier Guimard committed
89
    eval { require DBI; require DBD::SQLite; use Digest::SHA };
90
    if ($@) {
Xavier Guimard's avatar
Xavier Guimard committed
91
        skip 'DBD::SQLite not found', $maintests;
92 93 94 95
    }
    my $dbh = DBI->connect("dbi:SQLite:dbname=t/userdb.db");

    $dbh->do('CREATE TABLE users (user text,password text,name text)');
Xavier Guimard's avatar
Xavier Guimard committed
96

97 98
    # password secret1
    $dbh->do("INSERT INTO users VALUES ('dwho','secret1','Doctor who')");
Xavier Guimard's avatar
Xavier Guimard committed
99

100
    # password secret2
Xavier Guimard's avatar
Xavier Guimard committed
101 102 103 104
    $dbh->do(
"INSERT INTO users VALUES ('rtyler','{sha256}NSJNDTRl106FX41poTbnnHROo1pnXTOTNgoyfL9jWaI=','Rose Tyler')"
    );

105
    # password secret3
Xavier Guimard's avatar
Xavier Guimard committed
106 107 108
    $dbh->do(
"INSERT INTO users VALUES ('jsmith','{ssha512}wr0zU/I6f7U4bVoeOlJnNFbhF0a9np59LUeNnhokohVI/wiNzt8Y4JujfOfNQiGuiVgY+xrYggfmgpke6KdjxKS7W0GR1ZCe','John Smith')"
    );
109 110 111
    my $client = LLNG::Manager::Test->new(
        {
            ini => {
Xavier Guimard's avatar
Xavier Guimard committed
112 113 114 115 116 117 118 119 120 121 122 123 124
                logLevel                   => 'error',
                useSafeJail                => 1,
                authentication             => 'DBI',
                userDB                     => 'Same',
                dbiAuthChain               => 'dbi:SQLite:dbname=t/userdb.db',
                dbiAuthUser                => '',
                dbiAuthPassword            => '',
                dbiAuthTable               => 'users',
                dbiAuthLoginCol            => 'user',
                dbiAuthPasswordCol         => 'password',
                dbiAuthPasswordHash        => '',
                dbiDynamicHashEnabled      => 1,
                dbiDynamicHashValidSchemes => 'sha sha256 sha512',
125 126 127 128 129 130 131 132 133 134 135
                dbiDynamicHashValidSaltedSchemes => 'ssha ssha256 ssha512',
                dbiDynamicHashNewPasswordScheme  => 'ssha256',
                passwordDB                       => 'DBI',
                portalRequireOldPassword         => 1,
            }
        }
    );

    # Try to authenticate against plaintext password
    ok(
        $res = $client->_post(
Xavier Guimard's avatar
Xavier Guimard committed
136
            '/', IO::String->new('user=dwho&password=secret1'),
137 138 139 140 141 142 143 144 145 146 147
            length => 26
        ),
        'Authentication against plaintext password'
    );
    expectOK($res);
    my $id = expectCookie($res);
    $client->logout($id);

    # Try to authenticate against static hashed password
    ok(
        $res = $client->_post(
Xavier Guimard's avatar
Xavier Guimard committed
148
            '/', IO::String->new('user=rtyler&password=secret2'),
149 150 151 152 153
            length => 28
        ),
        'Authentication against static SHA-256 hashed password'
    );
    expectOK($res);
Xavier Guimard's avatar
Xavier Guimard committed
154
    $id = expectCookie($res);
155 156 157 158 159
    $client->logout($id);

    # Try to authenticate against salted password
    ok(
        $res = $client->_post(
Xavier Guimard's avatar
Xavier Guimard committed
160
            '/', IO::String->new('user=jsmith&password=secret3'),
161 162 163 164 165
            length => 28
        ),
        'Authentication against salted SHA-512 password'
    );
    expectOK($res);
Xavier Guimard's avatar
Xavier Guimard committed
166
    $id = expectCookie($res);
167 168 169 170 171 172

    # Try to modify password
    ok(
        $res = $client->_post(
            '/',
            IO::String->new(
173 174
'oldpassword=secret3&newpassword=secret4&confirmpassword=secret4'
            ),
175 176 177 178 179 180 181
            cookie => "lemonldap=$id",
            accept => 'application/json',
            length => 63
        ),
        'Change password'
    );
    expectOK($res);
182 183
    $client->logout($id);

184 185 186
    # Try to authenticate against new salted password
    ok(
        $res = $client->_post(
187
            '/', IO::String->new('user=jsmith&password=secret4'),
188 189 190 191 192
            length => 28
        ),
        'Authentication against newly-modified password'
    );
    expectOK($res);
Xavier Guimard's avatar
Xavier Guimard committed
193
    $id = expectCookie($res);
194

195
# Verify that password is hashed with correct scheme (dbiDynamicHashNewPasswordScheme)
196 197 198
    my $sth = $dbh->prepare("SELECT password FROM users WHERE user='jsmith';");
    $sth->execute();
    my $row = $sth->fetchrow_array;
199 200
    ok( $row =~ /^{ssha256}/,
        'Verify that password is hashed with correct scheme' );
201

202 203 204
    clean_sessions();
}
eval { unlink 't/userdb.db' };
Xavier Guimard's avatar
Xavier Guimard committed
205
count($maintests);
206
done_testing( count() );