SQL-Based Authentication

From Rosetta Code

Jump to: navigation, search

Programming Task
This is a programming task. It lays out a problem which Rosetta Code users are encouraged to solve, using languages they know.

Code examples should be formatted along the lines of one of the existing prototypes.

This task has three parts:

  • Connect to a MySQL database (connect_db)
  • Create user/password records in the following table (create_user)
  • Authenticate login requests against the table (authenticate_user)


This is the table definition:

create table users (
    userid int primary key auto_increment,
    username varchar(32) unique key not null,
    pass_salt tinyblob not null,  -- a string of 16 random bytes
    pass_md5 tinyblob not null
            -- binary MD5 hash of pass_salt concatenated with the password
);

(pass_salt and pass_md5 would be binary(16) values, but MySQL versions before 5.0.15 strip trailing spaces when selecting them.)

[edit] Perl

Works with: Perl version 5.8.8

Library: DBI
Library: DBD::mysql

 
 use DBI;
 
  # returns a database handle configured to throw an exception on query errors
 sub connect_db {
     my ($dbname, $host, $user, $pass) = @_;
     my $db = DBI->connect("dbi:mysql:$dbname:$host", $user, $pass)
         or die $DBI::errstr;
     $db->{RaiseError} = 1;
     $db
 }
 
  # if the user was successfully created, returns its user id.
  # if the name was already in use, returns undef.
 sub create_user {
     my ($db, $user, $pass) = @_;
     my $salt = pack "C*", map {int rand 256} 1..16;
     $db->do("INSERT IGNORE INTO users (username, pass_salt, pass_md5)
         VALUES (?, ?, unhex(md5(concat(pass_salt, ?))))",
         undef, $user, $salt, $pass)
       and $db->{mysql_insertid} or undef
 }
 
  # if the user is authentic, returns its user id.  otherwise returns undef.
 sub authenticate_user {
     my ($db, $user, $pass) = @_;
     my $userid = $db->selectrow_array("SELECT userid FROM users WHERE
         username=? AND pass_md5=unhex(md5(concat(pass_salt, ?)))",
         undef, $user, $pass);
     $userid
 }
 

[edit] Raven

MySQL connectivity is available out of the box. Below, we do not ever send the plain text password over the wire.

 'mysql://root@localhost/test' open as mysql
'abcdefghijklmnopqrstuvwxyz0123456789' as $salt_chars

# return userid for success and FALSE for failure.
define create_user use $user, $pass
    group 16 each as i
        $salt_chars choose chr
    join as $pass_salt
     "%($pass_salt)s%($pass)s" md5 as $pass_md5
    $user copy mysql escape as $user_name
    group 'INSERT IGNORE into users (username, pass_md5, pass_salt)'
        " VALUES ('%($user_name)s', unhex('%($pass_md5)s'), '%($pass_salt)s')"
    join mysql query inserted

# return userid for success and FALSE for failure.
define authenticate_user use $user, $pass
    FALSE as $userid
    $user copy mysql escape as $user_name
    group 'SELECT userid, pass_salt, hex(pass_md5)'
        " FROM users WHERE username = '%($user_name)s'"
    join mysql query as rs
    rs selected
    if  rs fetch values into $possible_userid, $pass_salt, $pass_md5
        "%($pass_salt)s%($pass)s" md5 $pass_md5 lower =
        if  $possible_userid as $userid
    $userid

'foo' 'bar' create_user       !if "could not create user\n"       print bye
'foo' 'bar' authenticate_user !if "could not authenticate user\n" print bye

"user successfully created and authenticated!\n" print
Personal tools