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, }, ) 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 } // }}}