1
0
mirror of https://github.com/aclindsa/moneygo.git synced 2024-10-30 15:50:04 -04:00
moneygo/internal/handlers/sessions.go
Aaron Lindsay 2ff1f47432 Remove duplicate *Tx versions of database access methods
Simplify naming to remove "Tx" now that all handlers only have access to
transactions anyway, and always use "tx" as the name of the variable
representing the SQL transactions (to make it less likely to cause
confusion with monetary transactions).
2017-10-14 19:41:13 -04:00

156 lines
3.2 KiB
Go

package handlers
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
)
type Session struct {
SessionId int64
SessionSecret string `json:"-"`
UserId int64
}
func (s *Session) Write(w http.ResponseWriter) error {
enc := json.NewEncoder(w)
return enc.Encode(s)
}
func (s *Session) Read(json_str string) error {
dec := json.NewDecoder(strings.NewReader(json_str))
return dec.Decode(s)
}
func GetSession(tx *Tx, r *http.Request) (*Session, error) {
var s Session
cookie, err := r.Cookie("moneygo-session")
if err != nil {
return nil, fmt.Errorf("moneygo-session cookie not set")
}
s.SessionSecret = cookie.Value
err = tx.SelectOne(&s, "SELECT * from sessions where SessionSecret=?", s.SessionSecret)
if err != nil {
return nil, err
}
return &s, nil
}
func DeleteSessionIfExists(tx *Tx, r *http.Request) error {
session, err := GetSession(tx, r)
if err == nil {
_, err := tx.Delete(session)
if err != nil {
return err
}
}
return nil
}
func NewSessionCookie() (string, error) {
bits := make([]byte, 128)
if _, err := io.ReadFull(rand.Reader, bits); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(bits), nil
}
type NewSessionWriter struct {
session *Session
cookie *http.Cookie
}
func (n *NewSessionWriter) Write(w http.ResponseWriter) error {
http.SetCookie(w, n.cookie)
return n.session.Write(w)
}
func NewSession(tx *Tx, r *http.Request, userid int64) (*NewSessionWriter, error) {
s := Session{}
session_secret, err := NewSessionCookie()
if err != nil {
return nil, err
}
cookie := http.Cookie{
Name: "moneygo-session",
Value: session_secret,
Path: "/",
Domain: r.URL.Host,
Expires: time.Now().AddDate(0, 1, 0), // a month from now
Secure: true,
HttpOnly: true,
}
s.SessionSecret = session_secret
s.UserId = userid
err = tx.Insert(&s)
if err != nil {
return nil, err
}
return &NewSessionWriter{&s, &cookie}, nil
}
func SessionHandler(r *http.Request, tx *Tx) ResponseWriterWriter {
if r.Method == "POST" || r.Method == "PUT" {
user_json := r.PostFormValue("user")
if user_json == "" {
return NewError(3 /*Invalid Request*/)
}
user := User{}
err := user.Read(user_json)
if err != nil {
return NewError(3 /*Invalid Request*/)
}
dbuser, err := GetUserByUsername(tx, user.Username)
if err != nil {
return NewError(2 /*Unauthorized Access*/)
}
user.HashPassword()
if user.PasswordHash != dbuser.PasswordHash {
return NewError(2 /*Unauthorized Access*/)
}
err = DeleteSessionIfExists(tx, r)
if err != nil {
log.Print(err)
return NewError(999 /*Internal Error*/)
}
sessionwriter, err := NewSession(tx, r, dbuser.UserId)
if err != nil {
log.Print(err)
return NewError(999 /*Internal Error*/)
}
return sessionwriter
} else if r.Method == "GET" {
s, err := GetSession(tx, r)
if err != nil {
return NewError(1 /*Not Signed In*/)
}
return s
} else if r.Method == "DELETE" {
err := DeleteSessionIfExists(tx, r)
if err != nil {
log.Print(err)
return NewError(999 /*Internal Error*/)
}
return SuccessWriter{}
}
return NewError(3 /*Invalid Request*/)
}