From a76c093d441ac0aa115a1f57be9e405384348be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20=C3=85hall?= Date: Fri, 16 Jun 2023 07:11:27 +0200 Subject: [PATCH] Session --- main.go | 52 ++++++++++++++++++++++++++++++++++++++ request_response.go | 17 +++++++++++++ session.go | 58 +++++++++++++++++++++++++++++++++++++++++++ static/index.html | 3 +-- static/js/app.mjs | 35 ++++++++++---------------- static/js/session.mjs | 51 +++++++++++++++++++++++++++++++++++++ static/session.mjs | 12 --------- 7 files changed, 192 insertions(+), 36 deletions(-) create mode 100644 static/js/session.mjs delete mode 100644 static/session.mjs diff --git a/main.go b/main.go index 0dd810b..8a75ee2 100644 --- a/main.go +++ b/main.go @@ -51,6 +51,8 @@ func main() {// {{{ static = http.FileServer(http.Dir("./static")) http.HandleFunc("/css_updated", cssUpdateHandler) http.HandleFunc("/session/create", sessionCreate) + http.HandleFunc("/session/retrieve", sessionRetrieve) + http.HandleFunc("/session/authenticate", sessionAuthenticate) http.HandleFunc("/ws", websocketHandler) http.HandleFunc("/", staticHandler) @@ -68,12 +70,62 @@ func sessionCreate(w http.ResponseWriter, r *http.Request) {// {{{ session, err := NewSession() if err != nil { responseError(w, err) + return } responseData(w, map[string]interface{}{ "OK": true, "Session": session, }) }// }}} +func sessionRetrieve(w http.ResponseWriter, r *http.Request) {// {{{ + var err error + var uuid string + + if uuid, err = sessionUUID(r); err != nil { + responseError(w, err) + return + } + + session := Session{ UUID: uuid } + if err = session.Retrieve(); err != nil { + responseError(w, err) + return + } + + responseData(w, map[string]interface{}{ + "OK": true, + "Session": session, + }) +}// }}} +func sessionAuthenticate(w http.ResponseWriter, r *http.Request) {// {{{ + var err error + var uuid string + + if uuid, err = sessionUUID(r); err != nil { + responseError(w, err) + return + } + + session := Session{ + UUID: uuid, + } + + req := struct{ Username string; Password string }{} + if err = request(r, &req); err != nil { + responseError(w, err) + return + } + + if err = session.Authenticate(req.Username, req.Password); err != nil { + responseError(w, err) + return + } + + responseData(w, map[string]interface{}{ + "OK": true, + "Session": session, + }) +}// }}} func websocketHandler(w http.ResponseWriter, r *http.Request) {// {{{ var err error diff --git a/request_response.go b/request_response.go index 9306e15..90e8ad0 100644 --- a/request_response.go +++ b/request_response.go @@ -3,6 +3,8 @@ package main import ( // Standard "encoding/json" + "errors" + "io" "net/http" ) @@ -26,3 +28,18 @@ func responseData(w http.ResponseWriter, data interface{}) { w.Header().Add("Content-Type", "application/json") w.Write(resJSON) } + +func request(r *http.Request, data interface{}) (err error) { + body, _ := io.ReadAll(r.Body) + defer r.Body.Close() + err = json.Unmarshal(body, data) + return +} + +func sessionUUID(r *http.Request) (string, error) { + headers := r.Header["X-Session-Id"] + if len(headers) > 0 { + return headers[0], nil + } + return "", errors.New("Invalid session") +} diff --git a/session.go b/session.go index 752457b..3aa27e8 100644 --- a/session.go +++ b/session.go @@ -3,6 +3,7 @@ package main import ( // Standard "database/sql" + "errors" "time" ) @@ -29,3 +30,60 @@ func NewSession() (session Session, err error) { return } + +func (session *Session) Retrieve() (err error) { + var rows *sql.Rows + if rows, err = db.Query(` + SELECT + uuid, user_id, created + FROM public.session + WHERE + uuid = $1 + `, + session.UUID, + ); err != nil { + return + } + defer rows.Close() + + if rows.Next() { + rows.Scan(&session.UUID, &session.UserID, &session.Created) + } + + return +} + +func (session *Session) Authenticate(username, password string) (err error) { + authenticated := false + + var rows *sql.Rows + if rows, err = db.Query(` + SELECT id + FROM public.user + WHERE + username=$1 AND + password=$2 + `, + username, + password, + ); err != nil { + return + } + defer rows.Close() + + if rows.Next() { + rows.Scan(&session.UserID) + authenticated = session.UserID > 0 + } + + if authenticated { + _, err = db.Exec("UPDATE public.session SET user_id=$1 WHERE uuid=$2", session.UserID, session.UUID) + if err != nil { + return + } + } else { + err = errors.New("Invalid authentication") + } + + return +} diff --git a/static/index.html b/static/index.html index 1cef8b4..dca9637 100644 --- a/static/index.html +++ b/static/index.html @@ -16,8 +16,7 @@ "preact/devtools": "/js/{{ .VERSION }}/lib/preact/devtools.mjs", "@preact/signals-core": "/js/{{ .VERSION }}/lib/signals/signals-core.mjs", "preact/signals": "/js/{{ .VERSION }}/lib/signals/signals.mjs", - "htm": "/js/{{ .VERSION }}/lib/htm/htm.mjs" - + "htm": "/js/{{ .VERSION }}/lib/htm/htm.mjs", "session": "/js/{{ .VERSION }}/session.mjs" } } diff --git a/static/js/app.mjs b/static/js/app.mjs index 3c86a6d..fc0e7b5 100644 --- a/static/js/app.mjs +++ b/static/js/app.mjs @@ -3,7 +3,7 @@ import 'preact/devtools' //import { signal } from 'preact/signals' import { h, Component, render, createRef } from 'preact' import htm from 'htm' -import Session from 'session' +import { Session } from 'session' const html = htm.bind(h) class App extends Component { @@ -15,10 +15,16 @@ class App extends Component { this.wsConnect() this.wsLoop() - + this.session = new Session(this) + this.session.initialize() }//}}} render() {//{{{ - if(this.sessionID === null) { + if(!this.session.initialized) { + return false; + //return html`
Validating session
` + } + + if(!this.session.authenticated()) { return html`<${Login} />` } @@ -85,8 +91,9 @@ class App extends Component { 'Content-Type': 'application/json', } - if(this.sessionID !== null) - headers['X-SESSION-ID'] = this.sessionID + console.log(this.session) + if(this.session.UUID !== '') + headers['X-Session-Id'] = this.session.UUID fetch(url, { method: 'POST', @@ -116,22 +123,6 @@ class App extends Component { break; } }//}}} - login(username, password) {//{{{ - this.request('/session/create', {}) - .then(res=>{ - this.setSessionID(res.Session.UUID) - }) - .catch(this.responseError) - }//}}} - - getSessionID() { - return window.localStorage.getItem("sessionID") - } - setSessionID(uuid) { - console.log('wut') - this.sessionID = uuid - window.localStorage.setItem('sessionID', uuid) - } } class Login extends Component { @@ -152,7 +143,7 @@ class Login extends Component { login() {//{{{ let username = document.getElementById('username').value let password = document.getElementById('password').value - window._app.current.login(username, password) + window._app.current.session.authenticate(username, password) }//}}} } diff --git a/static/js/session.mjs b/static/js/session.mjs new file mode 100644 index 0000000..f7aab33 --- /dev/null +++ b/static/js/session.mjs @@ -0,0 +1,51 @@ +export class Session { + constructor(app) { + this.app = app + this.UUID = '' + this.initialized = false + this.UserID = 0 + } + + initialize() {//{{{ + // Retrieving the stored session UUID, if any. + // If one found, validate with server. + let uuid = window.localStorage.getItem("session.UUID") + if(uuid === null) { + this.create() + } else { + this.UUID = uuid + this.app.request('/session/retrieve', {}) + .then(res=>{ + this.UserID = res.Session.UserID + this.initialized = true + this.app.forceUpdate() + }) + .catch(this.app.responseError) + } + }//}}} + create() {//{{{ + this.app.request('/session/create', {}) + .then(res=>{ + this.UUID = res.Session.UUID + window.localStorage.setItem('session.UUID', this.UUID) + this.app.forceUpdate() + }) + .catch(this.responseError) + }//}}} + authenticate(username, password) {//{{{ + this.app.request('/session/authenticate', { + username, + password, + }) + .then(res=>{ + this.UserID = res.Session.UserID + this.app.forceUpdate() + }) + .catch(this.app.responseError) + }//}}} + authenticated() {//{{{ + return this.UserID != 0 + }//}}} +} + +// vim: foldmethod=marker diff --git a/static/session.mjs b/static/session.mjs deleted file mode 100644 index d6c1dc7..0000000 --- a/static/session.mjs +++ /dev/null @@ -1,12 +0,0 @@ -export class Session { - static createFromStorage() { - let session = new Session() - session.UUID - } - - constructor() { - this.UUID = '' - this.authenticated = false - this.UserID = 0 - } -}