;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; CLOG - The Common Lisp Omnificent GUI                                 ;;;;
;;;; (c) David Botton                                                      ;;;;
;;;;                                                                       ;;;;
;;;; clog-auth.lisp                                                        ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; CLOG-Auth an authorization abstraction for CLOG

(mgl-pax:define-package :clog-auth
  (:documentation "CLOG-AUTH an authenticationa and authorization abstraction
for CLOG")
  (:use #:cl #:clog #:mgl-pax))

(cl:in-package :clog-auth)

(defsection @clog-auth (:title "CLOG Auth Objects")
  "CLOG-AUTH - Authentication"
  (get-authentication-token     function)
  (store-authentication-token   function)
  (remove-authentication-token  function)
  (set-on-authentication-change function)

  "CLOG-AUTH - Authorization"
  (add-authorization function)
  (is-authorized-p   function))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementation - clog-auth - Authenitcation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defparameter *clog-auth-key* "clog-auth-token"
  "Key used for local storage of authentication token")

(defparameter *authorization-hash* (make-hash-table* :test #'equalp)
  "Hash table of roles to actions")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; get-authentication-token ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun get-authentication-token (body &key auth-path)
  "Retrieve the stored authentication token"
  (check-type body clog-body)
  (let ((token (storage-element (window body) :local *clog-auth-key*)))
    (when (equalp token "null")
      (setf token nil))
    (unless token
      (when auth-path
        (url-assign (location body) auth-path)))
    token))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; store-authentication-token ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun store-authentication-token (body token)
  (check-type body clog-body)
  (setf (storage-element (window body) :local *clog-auth-key*) token))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; remove-authentication-token ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun remove-authentication-token (body)
  (check-type body clog-body)
  (storage-remove (window body) :local *clog-auth-key*))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; set-on-authentication-change ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun set-on-authentication-change (body handler)
  (check-type body clog-body)
  (set-on-storage (window body) (lambda (obj data)
                                  (declare (ignore obj))
                                  (set-on-storage (window body) nil)
                                  (when (equalp (getf data :key)
                                                *clog-auth-key*)
                                    (funcall handler body)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementation - clog-auth - Authorization
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defparameter *authorization-hash* (make-hash-table* :test #'equalp)
  "Hash table of role to action")

;;;;;;;;;;;;;;;;;;;;;;;
;; add-authorization ;;
;;;;;;;;;;;;;;;;;;;;;;;

(defun add-authorization (role-list action-list)
  "Add to each role in ROLE-LIST every action in ACTION-LIST"
  (dolist (role role-list)
    (dolist (action action-list)
      (setf (gethash role *authorization-hash*)
       (adjoin action (gethash role *authorization-hash*))))))

;;;;;;;;;;;;;;;;;;;;;
;; is-authorized-p ;;
;;;;;;;;;;;;;;;;;;;;;

(defun is-authorized-p (role-list action)
  "Given ROLE-LIST is action authorized. If action is nil returns t."
  (if action
      (dolist (role role-list nil)
        (when (member action (gethash role *authorization-hash*))
          (return t)))
      t))
