Add simplistic sessions and logout
The security of these sessions should be upgraded to verify that the user is actually allowed to look at the page(s) and that the given session hasn't expired, among other things.
This commit is contained in:
parent
5deba1e60d
commit
2767104527
1 changed files with 83 additions and 47 deletions
130
scrumelo.el
130
scrumelo.el
|
@ -10,6 +10,7 @@
|
||||||
;; A scrum web app.
|
;; A scrum web app.
|
||||||
|
|
||||||
(require 'cl-lib)
|
(require 'cl-lib)
|
||||||
|
(require 'eieio)
|
||||||
(require 'elnode)
|
(require 'elnode)
|
||||||
(require 'esxml)
|
(require 'esxml)
|
||||||
(require 'org)
|
(require 'org)
|
||||||
|
@ -52,6 +53,17 @@
|
||||||
"http://cdnjs.cloudflare.com/ajax/libs/react/0.3.2/JSXTransformer.js"
|
"http://cdnjs.cloudflare.com/ajax/libs/react/0.3.2/JSXTransformer.js"
|
||||||
"The location of the JSX Transformer JS file.")
|
"The location of the JSX Transformer JS file.")
|
||||||
|
|
||||||
|
(defvar scrumelo--sessions
|
||||||
|
(make-hash-table :test 'equal :size 6)
|
||||||
|
"Collection of session information.")
|
||||||
|
|
||||||
|
(defclass scrumelo-session ()
|
||||||
|
((email :accessor session-email)
|
||||||
|
(audience :accessor session-audience)
|
||||||
|
(expires :accessor session-expires)
|
||||||
|
(issuer :accessor session-issuer)
|
||||||
|
(status)))
|
||||||
|
|
||||||
(defmacro editing-scrumelo-story (id after &rest body)
|
(defmacro editing-scrumelo-story (id after &rest body)
|
||||||
"Edit the story with ID.
|
"Edit the story with ID.
|
||||||
|
|
||||||
|
@ -80,6 +92,14 @@ saving the buffer."
|
||||||
`(with-current-buffer (find-file-noselect scrumelo-project-file)
|
`(with-current-buffer (find-file-noselect scrumelo-project-file)
|
||||||
,@body))
|
,@body))
|
||||||
|
|
||||||
|
(defun scrumelo--json-to-session (persona-json)
|
||||||
|
"Turn PERSONA-JSON into a `scrumelo-session' object."
|
||||||
|
(let ((session (scrumelo-session "session")))
|
||||||
|
(mapc (lambda (cns)
|
||||||
|
(setf (slot-value session (car cns)) (cdr cns)))
|
||||||
|
persona-json)
|
||||||
|
session))
|
||||||
|
|
||||||
(defun scrumelo--css (href)
|
(defun scrumelo--css (href)
|
||||||
"Return a link pointing to HREF."
|
"Return a link pointing to HREF."
|
||||||
`(link (@ (href ,href) (rel "stylesheet") (type "text/css"))))
|
`(link (@ (href ,href) (rel "stylesheet") (type "text/css"))))
|
||||||
|
@ -102,22 +122,30 @@ saving the buffer."
|
||||||
(scrumelo--js scrumelo-jsxtransformer-js-location)
|
(scrumelo--js scrumelo-jsxtransformer-js-location)
|
||||||
(scrumelo--js "js/scrumelo.js")))
|
(scrumelo--js "js/scrumelo.js")))
|
||||||
|
|
||||||
|
(defun scrumelo--logged-in-p (httpcon)
|
||||||
|
"Check if the session on HTTPCON is logged-in."
|
||||||
|
(gethash (elnode-http-cookie httpcon "sessionid" t)
|
||||||
|
scrumelo--sessions))
|
||||||
|
|
||||||
(defun scrumelo-backlog-page (httpcon)
|
(defun scrumelo-backlog-page (httpcon)
|
||||||
"Send the backlog overview over HTTPCON."
|
"Send the backlog overview over HTTPCON."
|
||||||
(elnode-http-start httpcon 200 '("Content-Type" . "text/html"))
|
(if (not (scrumelo--logged-in-p httpcon))
|
||||||
(elnode-http-return
|
(elnode-send-redirect httpcon "/login")
|
||||||
httpcon
|
(elnode-send-html
|
||||||
(concat
|
httpcon
|
||||||
"<!DOCTYPE html>\n"
|
(concat
|
||||||
(sxml-to-xml
|
"<!DOCTYPE html>\n"
|
||||||
`(html (head (title "Scrumelo")
|
(sxml-to-xml
|
||||||
,@(scrumelo--css-list)
|
`(html (head (title "Scrumelo")
|
||||||
,@(scrumelo--js-list))
|
,@(scrumelo--css-list)
|
||||||
(body
|
,@(scrumelo--js-list))
|
||||||
(div (@ (class "container"))
|
(body
|
||||||
(h1 "Backlog")
|
(a (@ (href "/logout")) "Logout")
|
||||||
(div (@ (id "content")) "")
|
(div (@ (class "container"))
|
||||||
(script (@ (type "text/jsx") (src "js/main.js")) ""))))))))
|
(h1 "Backlog")
|
||||||
|
(div (@ (id "content")) "")
|
||||||
|
(script (@ (type "text/jsx")
|
||||||
|
(src "js/main.js")) "")))))))))
|
||||||
|
|
||||||
(defun scrumelo-new-story (httpcon)
|
(defun scrumelo-new-story (httpcon)
|
||||||
"Parse data from HTTPCON and write a new scrum story using it."
|
"Parse data from HTTPCON and write a new scrum story using it."
|
||||||
|
@ -208,39 +236,46 @@ saving the buffer."
|
||||||
|
|
||||||
(defun scrumelo-login-page (httpcon)
|
(defun scrumelo-login-page (httpcon)
|
||||||
"Show a login link for persona for HTTPCON."
|
"Show a login link for persona for HTTPCON."
|
||||||
(elnode-method httpcon
|
(if (scrumelo--logged-in-p httpcon)
|
||||||
(GET
|
(elnode-send-redirect httpcon "/")
|
||||||
(elnode-http-start httpcon 200 '("Content-Type" . "text/html"))
|
(elnode-method httpcon
|
||||||
(elnode-http-return
|
(GET
|
||||||
httpcon
|
(elnode-http-start httpcon 200 '("Content-Type" . "text/html"))
|
||||||
(concat
|
(elnode-http-return
|
||||||
"<!DOCTYPE html>\n"
|
httpcon
|
||||||
(sxml-to-xml
|
(concat
|
||||||
'(html (@ (lang "en"))
|
"<!DOCTYPE html>\n"
|
||||||
(head (meta (@ (charset "utf-8")))
|
(sxml-to-xml
|
||||||
(title "Login")
|
'(html (@ (lang "en"))
|
||||||
(script (@ (src "https://login.persona.org/include.js")) "")
|
(head (meta (@ (charset "utf-8")))
|
||||||
(script (@ (src "/js/login.js")) ""))
|
(title "Login")
|
||||||
(body
|
(script (@ (src "https://login.persona.org/include.js")) "")
|
||||||
(form (@ (id "login-form")
|
(script (@ (src "/js/login.js")) ""))
|
||||||
(method "POST")
|
(body
|
||||||
(action ""))
|
(form (@ (id "login-form")
|
||||||
(input (@ (id "assertion-field")
|
(method "POST")
|
||||||
(type "hidden")
|
(action ""))
|
||||||
(name "assertion")
|
(input (@ (id "assertion-field")
|
||||||
(value ""))))
|
(type "hidden")
|
||||||
(p (a (@ (href "javascript:login()")) "Login"))))))))
|
(name "assertion")
|
||||||
(POST
|
(value ""))))
|
||||||
(let* ((audience "http://localhost:8028")
|
(p (a (@ (href "javascript:login()")) "Login"))))))))
|
||||||
(assertion (elnode-http-param httpcon "assertion"))
|
(POST
|
||||||
(result (scrumelo--verify-credentials audience assertion)))
|
(let* ((audience "http://localhost:8028")
|
||||||
(if (equal (cdr (assoc 'status result)) "okay")
|
(assertion (elnode-http-param httpcon "assertion"))
|
||||||
(progn
|
(result (scrumelo--verify-credentials audience assertion)))
|
||||||
(elnode-http-start httpcon 302
|
(if (equal (cdr (assoc 'status result)) "okay")
|
||||||
'("Content-Type" . "text/html")
|
(progn
|
||||||
'("Location" . "/"))
|
(puthash (elnode-http-cookie httpcon "sessionid" t)
|
||||||
(elnode-send-redirect httpcon "/"))
|
(scrumelo--json-to-session result)
|
||||||
(elnode-send-status httpcon 403 "Not allowed"))))))
|
scrumelo--sessions)
|
||||||
|
(elnode-send-redirect httpcon "/"))
|
||||||
|
(elnode-send-status httpcon 403 "Not allowed")))))))
|
||||||
|
|
||||||
|
(defun scrumelo-logout (httpcon)
|
||||||
|
"Destroy the session on HTTPCON."
|
||||||
|
(remhash (elnode-http-cookie httpcon "sessionid" t) scrumelo--sessions)
|
||||||
|
(elnode-send-redirect httpcon "/login"))
|
||||||
|
|
||||||
(defun scrumelo-handler (httpcon)
|
(defun scrumelo-handler (httpcon)
|
||||||
"Send the right requests in HTTPCON to the right functions."
|
"Send the right requests in HTTPCON to the right functions."
|
||||||
|
@ -252,6 +287,7 @@ saving the buffer."
|
||||||
("^/js/login.js" . ,(elnode-make-send-file
|
("^/js/login.js" . ,(elnode-make-send-file
|
||||||
(concat scrumelo--base-dir "js/login.js")))
|
(concat scrumelo--base-dir "js/login.js")))
|
||||||
("^/login/$" . scrumelo-login-page)
|
("^/login/$" . scrumelo-login-page)
|
||||||
|
("^/logout/$" . scrumelo-logout)
|
||||||
("^/stories/$" . scrumelo-main-json)
|
("^/stories/$" . scrumelo-main-json)
|
||||||
("^/stories/new/$" . scrumelo-new-story)
|
("^/stories/new/$" . scrumelo-new-story)
|
||||||
("^/stories/state/$" . scrumelo-change-state)
|
("^/stories/state/$" . scrumelo-change-state)
|
||||||
|
|
Loading…
Reference in a new issue