|
- #|
-
- src/web/user.lisp
-
- User Interaction Frontend
-
- |#
-
- (in-package :cl-deck-builder2.web)
-
- ;; This seems to be okay. The intent of overriding HERMETIC::AUTHORIZE
- ;; is we use MITO-AUTH for user authentication and their class isn't
- ;; just a password hash. So this is an undocumented way of customizing
- ;; authorization method using HERMETIC.
- (defun hermetic::authorize (username password)
- (let ((object (funcall hermetic::*user-p* username)))
- (and object
- (string= (mito-auth:password-hash object)
- (mito-auth::make-password-hash
- password
- (mito-auth:password-salt object))))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defun already-logged-in-redirect ()
- "Tell the user they are already logged in, and send them to the home page."
- (progn
- (flash-error (format nil (_ "You are already logged in as: ~a")
- (user-name)))
- (redirect "/")))
-
- (defun user-exists-register-redirect ()
- "Tell the user that email address already is registered, and send them to the registration page."
- (progn
- (flash-error (_ "A user with that email is already registered."))
- (redirect "/user/register" 302)))
-
- (defun login-redirect (email password &optional (target "/"))
- "Try to log in using HERMETIC::LOGIN, and display an appropriate message upon success.
-
- TARGET may be set to the redirect target, and is provided from :REQUEST-PATH-INFO in *SESSION*."
- (handler-case
- (ratify:with-parsed-forms
- ((:email email)
- (:string password))
- (let ((params (list :|username| email :|password| password)))
- (login params
- (progn
- (flash-message (format nil (_ "Hello, ~a!")
- (user-name)))
- (redirect target))
- (progn
- (flash-error (_ "Incorrect password."))
- (redirect "/user/login"))
- (progn
- (flash-error (_ "No user corresponding to this email address."))
- (redirect "/user/login")))))
- (ratify:combined-error (e)
- (flash-error e)
- (redirect "/user/login" 302))))
-
- (defun register-new-user (name email password)
- "Try to register a new user by first SELECTing any matching USERs with EMAIL. If there is a match, use USER-EXISTS-REGISTER-REDIRECT.
- Otherwise, create a new user, display the appropriate message, and redirect."
- (handler-case
- (ratify:with-parsed-forms
- ((:string name)
- (:string password)
- (:email email))
- (with-connection (db)
- (let ((found (mito:find-dao 'user :email email)))
- ;; Try to select this username, if a username is returned, the
- ;; user already exists, and display error.
- ;;
- ;; Otherwise, create the user and add to the database.
- ;; Automatically direct to index.html on success.
- (if found
- (user-exists-register-redirect)
- (progn
- (if (create-user name email password)
- (progn
- (flash-message (_ "Success! Please log in."))
- (redirect "/user/login"))
- (progn
- (flash-message (_ "Something went wrong. Try again?"))
- (redirect "/user/register"))))))))
- (ratify:combined-error (e)
- (flash-error e)
- (flash-error (_ "Something went wrong. Try again?"))
- (redirect "/user/register"))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defun render-user-list (&optional (tpl #P"user/index.html"))
- "Helper function. Display User Index page."
- (render-with-env tpl
- `(:active "/user"
- :user-list ,(select-dao 'user))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defroute ("/user" :method :GET) ()
- "User Settings Main Index Page."
- (with-logged-in-user
- (render-with-env #P"user/index.html"
- `(:active "/user"))))
-
- (defroute ("/user/login" :method :GET) ()
- "User Login Route - require the user to log in."
- ;; Always display the USER/LOGIN page
- (if (ignore-errors
- (logged-in-p))
- (already-logged-in-redirect)
- (render-with-env #P"user/login.html"
- `(:active "/user"))))
-
- (defroute ("/user/login" :method :POST) (&key _parsed)
- "User Login POST Route. We use HERMETIC:LOGGED-IN-P to do all the heavy lifting. LOGIN-REDIRECT does the redirect."
- (v:info :web "POST /user/login => ~a" _parsed)
-
- ;; (v:info :web "REQUEST-PATH-INFO: ~a SESSION-REQUEST-PATH-INFO: ~a"
- ;; (lack.request:request-path-info *request*)
- ;; (gethash :request-path-info *session*))
-
- (if (logged-in-p)
- (already-logged-in-redirect)
- (login-redirect (query-param "email" _parsed)
- (query-param "password" _parsed)
- (gethash :request-path-info *session*))))
-
- ;; TODO successmessage and errormessages
- ;; TODO POST /user/logout ? Currently just GET /user/logout will log you out :P
- (defroute "/user/logout" ()
- "User Logout Route. GET request. Querying this from anywhere with the LACK.SESSION cookie set will trigger a logout and redirect."
- (logout
- (flash-message (_ "Logged out."))
- (flash-error (_ "You aren't logged in.")))
- (redirect "/" 302))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Register
- ;; TODO React To Session Cookie In default.html
- ;; Not sure what this is about^
- (defroute ("/user/register" :method :GET) ()
- "User Registration Main Route. Registration required!"
- (let ((logged-in (logged-in-p)))
- (if logged-in
- (already-logged-in-redirect)
- (render-with-env #P"user/register.html"
- `(:active "/user")))))
-
- (defroute ("/user/register" :method :POST) (&key _parsed)
- "User Registration POST Route. REGISTER-NEW-USER does all the work."
- (v:info :web "POST /user/register => ~a" _parsed)
-
- (let ((logged-in (logged-in-p)))
- (if logged-in
- (already-logged-in-redirect)
- (register-new-user (query-param "name" _parsed)
- (query-param "email" _parsed)
- (query-param "password" _parsed)))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defroute ("/user/:id/delete" :method :POST) (&key id)
- "User DELETE Route. ADMIN role only."
- (v:info :user "DELETE /user ~d" id)
- (handler-case
- (ratify:with-parsed-forms
- ((:integer id))
- (auth (:admin)
- (with-connection (db)
- (mito:delete-by-values 'user :id id)
- "")))
- (ratify:combined-error (e)
- (flash-error e))))
-
- (defroute ("/user/:id/roles" :method :GET) (&key id)
- "User Roles Route. ADMIN role only."
- (auth (:admin)
- (format nil "~{~s~^ ~}"
- (mapcar #'name-of
- (user-roles-of (find-dao 'user :id id))))))
-
- (defroute ("/user/admin" :method :GET) ()
- "Main User List Route. ADMIN role only."
- (auth (:admin)
- (render-user-list #P"user/admin.html")))
|