How to resolve the algorithm SQL-based authentication step by step in the Racket programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm SQL-based authentication step by step in the Racket programming language

Table of Contents

Problem Statement

This task has three parts: This is the table definition: (pass_salt and pass_md5 would be binary(16) values, but MySQL versions before 5.0.15 strip trailing spaces when selecting them.)

Let's start with the solution:

Step by Step solution about How to resolve the algorithm SQL-based authentication step by step in the Racket programming language

Source code in the racket programming language

#lang racket
(require db file/md5)
(define-logger authentication)
(current-logger authentication-logger)

(define DB-HOST "localhost")
(define DB-USER "devel")
(define DB-PASS "devel")	 
(define DB-NAME "test")	 

(define (connect-db)
  (mysql-connect
   #:user DB-USER
   #:database DB-NAME
   #:password DB-PASS))

(define (salt+password->hash salt password #:hex-encode? (hex-encode? #f))
  (md5 (bytes-append salt password) hex-encode?))

(define (report-sql-error e)
  (log-authentication-error "Failed to create user:~s" (exn-message e))
  #f)

(define (create-user db username passwd)
  ; if user was successfully created, returns its ID else #f
  (define salt (list->bytes (for/list ((i (in-range 16))) (random 256))))
  (define hash (salt+password->hash salt passwd))  
  (with-handlers ((exn:fail:sql? report-sql-error))
    (query db "INSERT INTO users (username, pass_salt, pass_md5) VALUES (?, ?, ?)"
           username salt hash)))

(define (authenticate-user db username password)
  (or
   (match (query-maybe-row db "SELECT pass_salt, pass_md5 FROM users WHERE username = ?" username)
     [#f #f]
     [(vector salt hash) (bytes=? hash (salt+password->hash salt password))])
   ; don't let the deviants know whether it's the username or password that's dodgy
   (error "the username, password combination does not exist in system")))

(module+ test
  (require rackunit)
  (define test-DB (connect-db))  
  ; typically, you only do this the once (or risk upsetting your users bigtime!)
  ; call this just the once!
  (define (create-users-table db)  
    (query-exec db "DROP TABLE IF EXISTS users")  
    (query-exec db #<<EOS
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
);
EOS
                ))  
  (create-users-table test-DB)    
  (create-user test-DB #"tim" #"shh! it's a secret!")  
  ; ensure the user exists (for testing purposes)
  (check-match (query-list test-DB "SELECT userid FROM users WHERE username = 'tim'") (list _))
  ; (ah... but tim exists!!!)
  (check-false (create-user test-DB #"tim" #"tim's password"))  
  (check-exn exn:fail? (λ () (authenticate-user test-DB #"tim" #"password")))
  (check-true (authenticate-user test-DB #"tim" #"shh! it's a secret!")))


  

You may also check:How to resolve the algorithm Boolean values step by step in the Icon and Unicon programming language
You may also check:How to resolve the algorithm Pig the dice game step by step in the XPL0 programming language
You may also check:How to resolve the algorithm Hello world/Text step by step in the GML programming language
You may also check:How to resolve the algorithm Leonardo numbers step by step in the Lua programming language
You may also check:How to resolve the algorithm Duffinian numbers step by step in the C++ programming language