webservice/session.go

155 lines
3.2 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 {
Username string
Password string
}
type AuthenticationResponse struct {
OK bool
Authenticated bool
UserID int
}
type AuthenticationHandler func(AuthenticationRequest, bool) (AuthenticationResponse, error)
type AuthorizationHandler func(*session.T, *http.Request) (bool, error)
func (service *Service) sessionNew(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
var session session.T
var found bool
var err error
for {
session.UUID = uuid.NewString()
if service.Db == nil {
if _, found = service.sessions[session.UUID]; found {
continue
}
session.Authenticated = false
session.Created = time.Now()
service.sessions[session.UUID] = &session
break
} else {
if _, found = service.retrieveSession(session.UUID); found {
continue
}
err = service.Db.NewSession(session.UUID)
if err != nil {
service.errorHandler(err, w)
return
}
break
}
}
service.logger.Info("session", "op", "new", "uuid", session.UUID)
respJSON, _ := json.Marshal(
struct {
OK bool
UUID string
}{
true,
session.UUID,
},
)
w.Write(respJSON)
} // }}}
func (service *Service) sessionAuthenticate(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
var authenticated 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, w)
return
}
// Authenticate against webservice user table if using a database.
var userID int
if service.Db != nil {
authenticated, userID, err = service.Db.Authenticate(authRequest.Username, authRequest.Password)
if err != nil {
service.errorHandler(err, w)
return
}
if authenticated && userID > 0 {
err = service.Db.SetSessionUser(sess.UUID, userID)
if err != nil {
service.errorHandler(err, w)
return
}
}
}
// 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, authenticated)
if err != nil {
service.errorHandler(err, w)
return
}
authResponse.UserID = userID
authResponse.OK = true
sess.Authenticated = authResponse.Authenticated
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
} // }}}