#lang scheme/base
(require "util.scm"
(planet "digest.ss" ("soegaard" "digest.plt" 1 2))
"form.scm"
"repository.scm"
"record.scm"
"web-support.scm"
"closures.scm"
"settings.scm"
"session.scm")
(provide current-user
logout-user!
if-login
get-user-rec
unauthenticated-login!
register-form
welcome-message
created-by?
created-by-str
stamp-user-on-rec!
if-these-users
)
(define-syntax if-these-users
(syntax-rules ()
((_ usernames sesh
then
else)
(let ((u (current-user sesh)))
(if (aand u (rec-prop u 'username) (member it (map ->string usernames)))
then
else)))))
(define (stamp-user-on-rec! rec user)
(rec-set-rec-prop! rec 'created_by user))
(define (get-user-rec username)
(load-one-where `((type . user) (username . ,username))))
(define (current-user sesh)
(aand (session-get-val sesh 'logged_in_as)
(load-rec it)))
(define (logout-user! sesh)
(session-remove-entry! sesh 'logged_in_as))
(define (unauthenticated-login! user-rec sesh)
(session-put-val! sesh 'logged_in_as (rec-id user-rec)))
(define (authenticated-login! username password sesh)
(aand (get-user-rec username)
(and (string=? (md5 (string->bytes/utf-8 (string-append password
(rec-prop it 'salt))))
(rec-prop it 'hashed-pass))
(begin (session-put-val! sesh 'logged_in_as (rec-id it))
it))))
(define-syntax if-login
(syntax-rules ()
((_ sesh (user-iden) then else)
(let ((user-iden (current-user sesh)))
(if user-iden
then
else)))))
(define (welcome-message sesh
#:redirect-to (url (setting *WEB_APP_URL*))
#:no-register (no-register #f)
#:on-login-success (on-login-success #f))
(if-login sesh (u)
`(group ,(format "Welcome, ~A " (rec-prop u 'username))
,(web-link "Sign out" (body-as-url r =>
(logout-user! sesh)
(redirect-to url))))
`(group ,(web-link "Sign in" (body-as-url r => (login-form sesh
#:on-success
on-login-success)))
,@(splice-if
(not no-register)
`(group " or "
,(web-link "Register"
(body-as-url
r =>
(register-form sesh
#:redirect-to url))))))))
(define (login-form sesh
#:redirect-to (url (setting *WEB_APP_URL*))
#:on-success (success-fn #f))
(form '((username "Username" text) (password "Password" password))
#:skip-save #t
#:on-done (lambda (login-rec)
(aif (authenticated-login! (rec-prop login-rec 'username)
(rec-prop login-rec 'password)
sesh)
(if success-fn (success-fn it) (redirect-to url))
`(em "Bad login.")))))
(define (register-form sesh #:redirect-to (url (setting *WEB_APP_URL*)))
(form '((username "Username" text) (password "Password" password)
(retype-password "Re-type password" password))
#:skip-save #t
#:validate (lambda (reg-data)
(if (and (string=? (rec-prop reg-data 'password)
(rec-prop reg-data 'retype-password)))
#f
"Passwords don't match."))
#:on-done (cut make-fresh-user <> sesh url)))
(define (make-fresh-user user-reg-rec sesh redirect-to-url)
(let* ((pass (rec-prop user-reg-rec 'password))
(salt (random-key-string 20))
(hashed-pass (md5 (string->bytes/utf-8 (string-append pass salt))))
(new-user (fresh-rec-from-data `((type . user)
(username . ,(rec-prop user-reg-rec 'username))
(hashed-pass . ,hashed-pass)
(salt . ,salt)))))
(store-rec! new-user)
(unauthenticated-login! new-user sesh)
(redirect-to redirect-to-url)))
(define (created-by? rec user-rec)
(aand (rec-prop rec 'created_by)
(string=? it (rec-id user-rec))))
(define (created-by-str rec)
(aif (aand (rec-rec-prop rec 'created_by) (rec-prop it 'username))
(format "by ~A" it)
""))