webservice/session.go

174 lines
3.8 KiB
Go

package webservice
import (
// External
"github.com/google/uuid"
// Internal
"git.gibonuddevalla.se/go/webservice/session"
// Standard
"encoding/json"
"io"
"net/http"
"time"
)
type AuthenticationRequest struct {
UserID int `json:"-"`
Username string
Password string
Additional interface{}
}
type AuthenticationResponse struct {
OK bool
Authenticated bool
UserID int
MFA any
}
type AuthenticationHandler func(AuthenticationRequest, *session.T, bool) (AuthenticationResponse, error)
type AuthorizationHandler func(*session.T, *http.Request) (bool, error)
func (service *Service) sessionNew(w http.ResponseWriter, r *http.Request, foo *session.T) { // {{{
var sess session.T
var found bool
var err error
for {
sess.UUID = uuid.NewString()
if service.Db == nil {
if _, found = service.sessions[sess.UUID]; found {
continue
}
sess.Authenticated = false
sess.Created = time.Now()
service.sessions[sess.UUID] = &sess
break
} else {
if _, found = service.RetrieveSession(sess.UUID); found {
continue
}
err = service.Db.NewSession(sess.UUID)
if err != nil {
service.errorHandler(err, "001-A001", w)
return
}
break
}
}
service.logger.Info("session", "op", "new", "uuid", sess.UUID)
respJSON, _ := json.Marshal(
struct {
OK bool
Session session.T
}{
true,
sess,
},
)
cookie := http.Cookie{}
cookie.Name = "X-Session-ID"
cookie.Value = sess.UUID
cookie.MaxAge = 86400 * 365
cookie.Path = "/"
http.SetCookie(w, &cookie)
w.Write(respJSON)
} // }}}
func (service *Service) sessionAuthenticate(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
var authenticatedByFramework bool
var authResponse AuthenticationResponse
var err error
reqBody, _ := io.ReadAll(r.Body)
// Username and password are provided in this JSON authentication request.
var authRequest AuthenticationRequest
err = json.Unmarshal(reqBody, &authRequest)
if err != nil {
service.errorHandler(err, "001-0004", w)
return
}
// Authenticate against webservice user table if using a database.
var userID int = sess.UserID
if service.Db != nil && userID == 0 {
authenticatedByFramework, userID, err = service.Db.Authenticate(authRequest.Username, authRequest.Password)
if err != nil {
service.errorHandler(err, "001-A002", w)
return
}
authRequest.UserID = userID
}
// The authentication handler is provided with the authenticated response of the possible database authentication,
// and given a chance to override it.
authResponse, err = service.authenticationHandler(authRequest, sess, authenticatedByFramework)
if err != nil {
service.errorHandler(err, "001-F002", w)
return
}
authResponse.UserID = userID
authResponse.OK = true
sess.Authenticated = authResponse.Authenticated
if authResponse.MFA != nil {
err = service.Db.SetSessionMFA(sess.UUID, authResponse.MFA)
if err != nil {
service.errorHandler(err, "001-A003", w)
return
}
}
if authResponse.Authenticated && userID > 0 {
err = service.Db.SetSessionUser(sess.UUID, userID)
if err != nil {
service.errorHandler(err, "001-A003", w)
return
}
service.Db.UpdateUserTime(userID)
}
authResp, _ := json.Marshal(authResponse)
w.Write(authResp)
} // }}}
func (service *Service) sessionRetrieve(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
response := struct {
OK bool
Session *session.T
}{
true,
sess,
}
out, _ := json.Marshal(response)
w.Write(out)
} // }}}
func (service *Service) RetrieveSession(uuid string) (session *session.T, found bool) { // {{{
var err error
if service.Db == nil {
session, found = service.sessions[uuid]
return
}
session, err = service.Db.RetrieveSession(uuid)
if err != nil {
service.logger.Error("session", "error", err)
session = nil
return
}
found = (session != nil)
return
} // }}}