Add basic-auth to notebook example (#314)
This commit is contained in:
parent
5f6b26e7f3
commit
d73144bd4c
1 changed files with 94 additions and 36 deletions
|
|
@ -5,7 +5,11 @@
|
|||
'[clojure.java.shell :refer [sh]]
|
||||
'[clojure.string :as str])
|
||||
|
||||
(def debug? false)
|
||||
(def debug? true)
|
||||
(def user "admin")
|
||||
(def password "admin")
|
||||
(def base64 (-> (.getEncoder java.util.Base64)
|
||||
(.encodeToString (.getBytes (str user ":" password)))))
|
||||
|
||||
(def notes-file (io/file (System/getProperty "user.home") ".notes" "notes.txt"))
|
||||
(io/make-parents notes-file)
|
||||
|
|
@ -36,9 +40,24 @@
|
|||
(str/join " " (map html v))
|
||||
:else (str v)))
|
||||
|
||||
(defn write-response [out session-id status headers content]
|
||||
(let [cookie-header (str "Set-Cookie: notes-id=" session-id)
|
||||
headers (str/join "\r\n" (conj headers cookie-header))
|
||||
response (str "HTTP/1.1 " status "\r\n"
|
||||
(str headers "\r\n")
|
||||
"Content-Length: " (if content (count content)
|
||||
0)
|
||||
"\r\n\r\n"
|
||||
(when content
|
||||
(str content)))]
|
||||
(when debug? (println response))
|
||||
(binding [*out* out]
|
||||
(print response)
|
||||
(flush))))
|
||||
|
||||
;; the home page
|
||||
(defn home []
|
||||
(str
|
||||
(defn home-response [out session-id]
|
||||
(let [body (str
|
||||
"<!DOCTYPE html>\n"
|
||||
(html
|
||||
[:html
|
||||
|
|
@ -49,7 +68,42 @@
|
|||
[:pre (slurp notes-file)]
|
||||
[:form {:action "/" :method "post"}
|
||||
[:input {:type "text" :name "note"}]
|
||||
[:input {:type "submit" :value "Submit"}]]]])))
|
||||
[:input {:type "submit" :value "Submit"}]]]]))]
|
||||
(write-response out session-id "200 OK" nil body)))
|
||||
|
||||
(defn basic-auth-response [out session-id]
|
||||
(write-response out session-id
|
||||
"401 Unauthorized"
|
||||
["WWW-Authenticate: Basic realm=\"notes\""]
|
||||
nil))
|
||||
|
||||
(def known-sessions
|
||||
(atom #{}))
|
||||
|
||||
(defn new-session! []
|
||||
(let [uuid (str (java.util.UUID/randomUUID))]
|
||||
(swap! known-sessions conj uuid)
|
||||
uuid))
|
||||
|
||||
(defn get-session-id [headers]
|
||||
(if-let [cookie-header (first (filter #(str/starts-with? % "Cookie: ") headers))]
|
||||
(let [parts (str/split cookie-header #"; ")]
|
||||
(if-let [notes-id (first (filter #(str/starts-with? % "notes-id") parts))]
|
||||
(str/replace notes-id "notes-id=" "")
|
||||
(new-session!)))
|
||||
(new-session!)))
|
||||
|
||||
(defn basic-auth-header [headers]
|
||||
(some #(str/starts-with? % "Basic-Auth: ") headers))
|
||||
|
||||
(def authenticated-sessions
|
||||
(atom #{}))
|
||||
|
||||
(defn authenticate! [session-id headers]
|
||||
(or (contains? @authenticated-sessions session-id)
|
||||
(when (some #(= % (str "Authorization: Basic " base64)) headers)
|
||||
(swap! authenticated-sessions conj session-id)
|
||||
true)))
|
||||
|
||||
;; run the server
|
||||
(with-open [server-socket (let [s (new ServerSocket 8080)]
|
||||
|
|
@ -60,12 +114,14 @@
|
|||
(let [out (io/writer (.getOutputStream client-socket))
|
||||
is (.getInputStream client-socket)
|
||||
in (io/reader is)
|
||||
response (loop [headers []]
|
||||
[_req & headers :as response]
|
||||
(loop [headers []]
|
||||
(let [line (.readLine in)]
|
||||
(if (str/blank? line)
|
||||
headers
|
||||
(recur (conj headers line)))))
|
||||
data (let [sb (StringBuilder.)]
|
||||
session-id (get-session-id headers)
|
||||
form-data (let [sb (StringBuilder.)]
|
||||
(loop []
|
||||
(when (.ready in)
|
||||
(.append sb (char (.read in)))
|
||||
|
|
@ -73,15 +129,17 @@
|
|||
(-> (str sb)
|
||||
(java.net.URLDecoder/decode)))
|
||||
_ (when debug? (println (str/join "\n" response)))
|
||||
_ (when-not (str/blank? data)
|
||||
(when debug? (println data))
|
||||
(let [note (str/replace data "note=" "")]
|
||||
_ (when-not (str/blank? form-data)
|
||||
(when debug? (println form-data))
|
||||
(let [note (str/replace form-data "note=" "")]
|
||||
(spit notes-file (str note "\n") :append true)))
|
||||
_ (when debug? (println))
|
||||
body (home)]
|
||||
(.write out (format "HTTP/1.1 %s OK\r\nContent-Length: %s\r\n\r\n%s"
|
||||
200
|
||||
(count body)
|
||||
body))
|
||||
(.flush out))
|
||||
_ (when debug? (println))]
|
||||
(cond
|
||||
;; if we didn't see this session before, we want the user to re-authenticate
|
||||
(not (contains? @known-sessions session-id))
|
||||
(let [uuid (new-session!)]
|
||||
(basic-auth-response out uuid))
|
||||
(not (authenticate! session-id headers))
|
||||
(basic-auth-response out session-id)
|
||||
:else (home-response out session-id)))
|
||||
(recur)))
|
||||
|
|
|
|||
Loading…
Reference in a new issue