Skip to content

Commit

Permalink
abstract auth-db to an auth-test with a default using the auth-db
Browse files Browse the repository at this point in the history
  • Loading branch information
nicferrier committed Nov 6, 2012
1 parent 9f3f654 commit bad886a
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 57 deletions.
68 changes: 29 additions & 39 deletions elnode-tests.el
Expand Up @@ -1154,34 +1154,6 @@ Test that we get the right chunked encoding stuff going on."
(defvar elnode-test-wrapped-handler-counter 0) (defvar elnode-test-wrapped-handler-counter 0)
(defvar elnode-test-wrapping-handler-counter 0) (defvar elnode-test-wrapping-handler-counter 0)


(ert-deftest elnode--wrap-handler ()
"Test wrapping a handler held in a symbol with another."
(let ((elnode-test-wrapped-handler-counter 0)
(elnode-test-wrapping-handler-counter 0))
;; Define the handler we'll wrap
(flet ((wrap-test-handler (httpcon)
(incf elnode-test-wrapped-handler-counter)))
(unwind-protect
(progn
;; Wrap that handler
(elnode--wrap-handler
'wrap-test-handler
"testit/$"
(lambda (httpcon &rest args)
(incf elnode-test-wrapping-handler-counter)))
(with-elnode-mock-server 'wrap-test-handler t
;; Call something to invoke the wrapper
(elnode-test-call "/something/")
(should (equal elnode-test-wrapped-handler-counter 1))
(should (equal elnode-test-wrapping-handler-counter 0))
(elnode-test-call "/testit/")
(should (equal elnode-test-wrapped-handler-counter 1))
(should (equal elnode-test-wrapping-handler-counter 1))))
;; We need to clear the symbol and it's plist whenever the
;; test runs, otherwise we keep state in the symbol.
(setplist 'wrap-test-handler nil)
(unintern 'wrap-test-handler)))))



;; Elnode auth tests ;; Elnode auth tests


Expand All @@ -1205,53 +1177,71 @@ default is `elnode-auth-db')."
"Check the authentication check. "Check the authentication check.
This tests the authentication database check." This tests the authentication database check."
(let ((elnode-auth-db (elnode-db-make '(elnode-db-hash)))) (let* ((elnode-auth-db (elnode-db-make '(elnode-db-hash)))
;; auth test
(auth-test
(lambda (username)
(elnode-auth-default-test username 'elnode-auth-db))))
;; The only time we really need clear text passwords is when ;; The only time we really need clear text passwords is when
;; faking records for test ;; faking records for test
(elnode--auth-init-user-db '(("nferrier" . "password") (elnode--auth-init-user-db '(("nferrier" . "password")
("someuser" . "secret"))) ("someuser" . "secret")))
(should (should
(elnode-auth-user-p "someuser" "secret")))) (elnode-auth-user-p "someuser" "secret" :auth-test auth-test))))


(ert-deftest elnode-auth-check-p () (ert-deftest elnode-auth-check-p ()
"Test basic login. "Test basic login.
Tess that we can login a user and then assert that they are Tess that we can login a user and then assert that they are
authenticated." authenticated."
(let ((elnode-loggedin-db (make-hash-table :test 'equal)) (let* ((elnode-loggedin-db (make-hash-table :test 'equal))
(elnode-auth-db (elnode-db-make '(elnode-db-hash)))) (elnode-auth-db (elnode-db-make '(elnode-db-hash)))
;; Make an auth-test function
(auth-test
(lambda (username)
(elnode-auth-default-test username 'elnode-auth-db))))
;; The only time we really need clear text passwords is when ;; The only time we really need clear text passwords is when
;; faking records for test ;; faking records for test
(elnode--auth-init-user-db '(("nferrier" . "password") (elnode--auth-init-user-db '(("nferrier" . "password")
("someuser" "secret"))) ("someuser" "secret")))
;; Test a failure
;; Test a failure
(should (should
(equal "an error occured!" (equal "an error occured!"
(condition-case credentials (condition-case credentials
(elnode-auth-login "nferrier" "secret") (elnode-auth-login
"nferrier" "secret"
:auth-test auth-test)
(elnode-auth (elnode-auth
"an error occured!")))) "an error occured!"))))


;; Now test ;; Now test
(let ((hash (elnode-auth-login "nferrier" "password"))) (let ((hash (elnode-auth-login
"nferrier" "password" :auth-test auth-test)))
(should (elnode-auth-check-p "nferrier" hash))))) (should (elnode-auth-check-p "nferrier" hash)))))


(ert-deftest elnode-auth-cookie-check-p () (ert-deftest elnode-auth-cookie-check-p ()
"Check that a cookie can be used for auth." "Check that a cookie can be used for auth."
(let ((elnode-loggedin-db (make-hash-table :test 'equal)) (let* ((elnode-loggedin-db (make-hash-table :test 'equal))
(elnode-auth-db (elnode-db-make '(elnode-db-hash)))) (elnode-auth-db (elnode-db-make '(elnode-db-hash)))
;; auth-test function
(auth-test
(lambda (username)
(elnode-auth-default-test username 'elnode-auth-db))))
;; The only time we really need clear text passwords is when ;; The only time we really need clear text passwords is when
;; faking records for test ;; faking records for test
(elnode--auth-init-user-db '(("nferrier" . "password") (elnode--auth-init-user-db '(("nferrier" . "password")
("someuser" "secret"))) ("someuser" "secret")))
;; Now test ;; Now test
(let ((hash (elnode-auth-login "nferrier" "password"))) (let ((hash (elnode-auth-login
"nferrier" "password" :auth-test auth-test)))
(fakir-mock-process :httpcon (fakir-mock-process :httpcon
((:elnode-http-header ((:elnode-http-header
`(("Cookie" . ,(concat "elnode-auth=nferrier::" hash))))) `(("Cookie" . ,(concat "elnode-auth=nferrier::" hash)))))
(should (elnode-auth-cookie-check-p :httpcon)))) (should (elnode-auth-cookie-check-p :httpcon))))
;; Test what happens without a cookie ;; Test what happens without a cookie
(let ((hash (elnode-auth-login "nferrier" "password"))) (let ((hash (elnode-auth-login
"nferrier" "password" :auth-test auth-test)))
(fakir-mock-process :httpcon (fakir-mock-process :httpcon
((:elnode-http-header ((:elnode-http-header
`(("Referer" . "http://somehost.example.com")))) `(("Referer" . "http://somehost.example.com"))))
Expand Down
63 changes: 45 additions & 18 deletions elnode.el
Expand Up @@ -3190,16 +3190,17 @@ main `elnode-auth-db' is used."


(defun* elnode-auth-user-p (username (defun* elnode-auth-user-p (username
password password
&key (auth-db elnode-auth-db)) &key
"Is the user in the AUTH-DB? auth-test)
"Does the AUTH-TEST pass?
The password is stored in the db hashed keyed by the USERNAME, The password is stored in the db hashed keyed by the USERNAME,
this looks up and tests the hash. this looks up and tests the hash.
The AUTH-DB is an `db', by default it is The AUTH-DB is an `db', by default it is
`elnode-auth-db'" `elnode-auth-db'"
(let ((token (elnode--auth-make-hash username password))) (let ((token (elnode--auth-make-hash username password)))
(equal token (db-get username auth-db)))) (equal token (funcall auth-test username))))




(defvar elnode-loggedin-db (make-hash-table :test 'equal) (defvar elnode-loggedin-db (make-hash-table :test 'equal)
Expand Down Expand Up @@ -3228,7 +3229,7 @@ See `elnode-auth-login' for how this is updated.")
(defun* elnode-auth-login (username (defun* elnode-auth-login (username
password password
&key &key
(auth-db elnode-auth-db) auth-test
(loggedin-db elnode-loggedin-db)) (loggedin-db elnode-loggedin-db))
"Log a user in. "Log a user in.
Expand All @@ -3240,7 +3241,8 @@ Takes optional AUTH-DB which is the database variable to
use (which is `elnode-auth-db' by default) and LOGGEDIN-DB which use (which is `elnode-auth-db' by default) and LOGGEDIN-DB which
is the logged-in state database to use and which is is the logged-in state database to use and which is
`elnode-loggedin-db' by default." `elnode-loggedin-db' by default."
(if (elnode-auth-user-p username password :auth-db auth-db) ;; FIXME - pass in the test function
(if (elnode-auth-user-p username password :auth-test auth-test)
(let* ((rndstr (format "%d" (random))) (let* ((rndstr (format "%d" (random)))
(hash (sha1 (format "%s:%s:%s" (hash (sha1 (format "%s:%s:%s"
username username
Expand Down Expand Up @@ -3304,11 +3306,14 @@ See `elnode-auth-cookie-check-p' for more details."
;; Not sure this is the correct token... ;; Not sure this is the correct token...
(signal 'elnode-auth-token :not-logged-in))) (signal 'elnode-auth-token :not-logged-in)))


(defvar elnode-auth-httpcon nil
"Dynamic scope variable for HTTP con while we auth.")

(defun* elnode-auth-http-login (httpcon (defun* elnode-auth-http-login (httpcon
username password logged-in username password logged-in
&key &key
(cookie-name "elnodeauth") (cookie-name "elnodeauth")
(auth-db elnode-auth-db) auth-test
(loggedin-db elnode-loggedin-db)) (loggedin-db elnode-loggedin-db))
"Log the USERNAME in on the HTTPCON if PASSWORD is correct. "Log the USERNAME in on the HTTPCON if PASSWORD is correct.
Expand All @@ -3320,11 +3325,12 @@ Actually uses `elnode-auth-login' to do the assertion.
AUTH-DB is a database, by default `elnode-auth-db', it's passed AUTH-DB is a database, by default `elnode-auth-db', it's passed
to `elnode-auth-login'." to `elnode-auth-login'."
(let ((hash (let* ((elnode-auth-httpcon httpcon)
(elnode-auth-login (hash
username password (elnode-auth-login
:auth-db auth-db username password
:loggedin-db loggedin-db))) :auth-test auth-test
:loggedin-db loggedin-db)))
(elnode-http-header-set (elnode-http-header-set
httpcon httpcon
(elnode-http-cookie-make (elnode-http-cookie-make
Expand Down Expand Up @@ -3372,7 +3378,7 @@ This function sends the contents of the custom variable
(defun* elnode-auth--wrapping-login-handler (httpcon (defun* elnode-auth--wrapping-login-handler (httpcon
sender target sender target
&key &key
(auth-db elnode-auth-db) auth-test ; assert not nil?
(cookie-name "elnodeauth") (cookie-name "elnodeauth")
(loggedin-db elnode-loggedin-db)) (loggedin-db elnode-loggedin-db))
"The implementation of the login handler for wrapping. "The implementation of the login handler for wrapping.
Expand All @@ -3391,15 +3397,19 @@ This receives the SENDER and the TARGET from the wrapper spec."
(elnode-auth-http-login (elnode-auth-http-login
httpcon httpcon
username password logged-in username password logged-in
:cookie-name cookie-name :auth-test auth-test
:auth-db auth-db) :cookie-name cookie-name)
(elnode-auth-credentials (elnode-auth-credentials
(elnode-send-redirect (elnode-send-redirect
httpcon httpcon
(if (not logged-in) (if (not logged-in)
target target
(format "%s?redirect=%s" target logged-in))))))))) (format "%s?redirect=%s" target logged-in)))))))))


(defun elnode-auth-default-test (username database)
"The default test function used for Elnode auth."
(db-get username (symbol-value database)))

(defun* elnode-auth-make-login-wrapper (wrap-target (defun* elnode-auth-make-login-wrapper (wrap-target
&key &key
(sender 'elnode-auth-login-sender) (sender 'elnode-auth-login-sender)
Expand Down Expand Up @@ -3435,11 +3445,20 @@ being the symbol `:elnode-wrapper-spec'."
wrap-target wrap-target
(lambda (httpcon &optional args) (lambda (httpcon &optional args)
(destructuring-bind (&key (auth-db 'elnode-auth-db) (destructuring-bind (&key (auth-db 'elnode-auth-db)
auth-test ; nil by default
(cookie-name "elnodeauth")) args (cookie-name "elnodeauth")) args
(elnode-auth--wrapping-login-handler (let ((auth-test-fn
httpcon sender target (if auth-test
:auth-db (symbol-value auth-db) auth-test
:cookie-name cookie-name))) ;; Else make a default one of the database
(lambda (username)
(elnode-auth-default-test username auth-db)))))
(elnode-auth--wrapping-login-handler
httpcon sender target
;; FIXME - possibly have test function here
;; as well as default test function
:auth-test auth-test-fn
:cookie-name cookie-name))))
target)) target))


(defvar elnode--defined-authentication-schemes (defvar elnode--defined-authentication-schemes
Expand All @@ -3448,6 +3467,7 @@ being the symbol `:elnode-wrapper-spec'."


(defun* elnode--auth-define-scheme-do-wrap (wrapper-spec (defun* elnode--auth-define-scheme-do-wrap (wrapper-spec
&key &key
auth-test
(auth-db 'elnode-auth-db) (auth-db 'elnode-auth-db)
(cookie-name "elnodeauth")) (cookie-name "elnodeauth"))
"Setup the auth wrapping. "Setup the auth wrapping.
Expand All @@ -3460,12 +3480,14 @@ The WRAPPER-SPEC is used to setup the wrapping.
The AUTH-DB and the COOKIE-NAME are passed to the wrapper." The AUTH-DB and the COOKIE-NAME are passed to the wrapper."
(destructuring-bind (func match-handler match-path) wrapper-spec (destructuring-bind (func match-handler match-path) wrapper-spec
(elnode-set-wrapper func match-handler match-path (elnode-set-wrapper func match-handler match-path
:auth-test auth-test
:auth-db auth-db :auth-db auth-db
:cookie-name cookie-name))) :cookie-name cookie-name)))


(defmacro* elnode-auth-define-scheme (scheme-name (defmacro* elnode-auth-define-scheme (scheme-name
&key &key
(test :cookie) (test :cookie)
auth-test
(auth-db 'elnode-auth-db) (auth-db 'elnode-auth-db)
(cookie-name "elnodeauth") (cookie-name "elnodeauth")
(failure-type :redirect) (failure-type :redirect)
Expand All @@ -3488,6 +3510,10 @@ AUTH-DB is the `db' used for authentication information.
It is used as the authority of information on users. By default It is used as the authority of information on users. By default
this is `elnode-auth-db'. this is `elnode-auth-db'.
AUTH-TEST is a function to implement retrieval of users. It is
used in preference to AUTH-DB but can be nil in which case a
default based on AUTH-DB will be used.
FAILURE-TYPE is what to do if authentication fails. Currently FAILURE-TYPE is what to do if authentication fails. Currently
only `:redirect' is supported. To redirect on failure means to only `:redirect' is supported. To redirect on failure means to
send a 302 with a location to visit a login page. :FAILURE-TYPE send a 302 with a location to visit a login page. :FAILURE-TYPE
Expand Down Expand Up @@ -3516,6 +3542,7 @@ should indicate a path where a user can login, for example
(eq :elnode-wrapper-spec (car ,redirect-specv))) (eq :elnode-wrapper-spec (car ,redirect-specv)))
(elnode--auth-define-scheme-do-wrap (elnode--auth-define-scheme-do-wrap
(cdr ,redirect-specv) (cdr ,redirect-specv)
:auth-test ,auth-test
:auth-db ,auth-dbv :auth-db ,auth-dbv
:cookie-name ,cookie-namev)) :cookie-name ,cookie-namev))
((not (stringp ,redirect-specv)) ((not (stringp ,redirect-specv))
Expand Down

0 comments on commit bad886a

Please sign in to comment.