1
0
Fork 0
lunch/users.go

233 lines
4.5 KiB
Go

package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
)
type User struct {
UserId int64
GroupId int64
GroupPassword string `db:"-"`
Name string
Username string
Password string `db:"-"`
PasswordHash string `json:"-"`
Email string
}
const BogusPassword = "password"
type UserExistsError struct{}
func (ueu UserExistsError) Error() string {
return "User exists"
}
func (u *User) Write(w http.ResponseWriter) error {
enc := json.NewEncoder(w)
return enc.Encode(u)
}
func (u *User) Read(json_str string) error {
dec := json.NewDecoder(strings.NewReader(json_str))
return dec.Decode(u)
}
func (u *User) HashPassword() {
password_hasher := sha256.New()
io.WriteString(password_hasher, u.Password)
u.PasswordHash = fmt.Sprintf("%x", password_hasher.Sum(nil))
u.Password = ""
}
func GetUser(userid int64) (*User, error) {
var u User
err := DB.SelectOne(&u, "SELECT * from users where UserId=?", userid)
if err != nil {
return nil, err
}
return &u, nil
}
func GetUserByUsername(username string) (*User, error) {
var u User
err := DB.SelectOne(&u, "SELECT * from users where Username=?", username)
if err != nil {
return nil, err
}
return &u, nil
}
func InsertUser(u *User) error {
transaction, err := DB.Begin()
if err != nil {
return err
}
existing, err := transaction.SelectInt("SELECT count(*) from users where Username=?", u.Username)
if err != nil {
transaction.Rollback()
return err
}
if existing > 0 {
transaction.Rollback()
return UserExistsError{}
}
err = transaction.Insert(u)
if err != nil {
transaction.Rollback()
return err
}
err = transaction.Commit()
if err != nil {
transaction.Rollback()
return err
}
return nil
}
func GetUserFromSession(r *http.Request) (*User, error) {
s, err := GetSession(r)
if err != nil {
return nil, err
}
return GetUser(s.UserId)
}
func UserHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
user_json := r.PostFormValue("user")
if user_json == "" {
WriteError(w, 3 /*Invalid Request*/)
return
}
var user User
err := user.Read(user_json)
if err != nil {
WriteError(w, 3 /*Invalid Request*/)
return
}
user.UserId = -1
user.HashPassword()
validgroup, err := ValidGroup(user.GroupId, user.GroupPassword)
if err != nil {
WriteError(w, 3 /*Invalid Request*/)
return
} else if !validgroup {
WriteError(w, 2 /*Unauthorized Access*/)
return
}
err = InsertUser(&user)
if err != nil {
if _, ok := err.(UserExistsError); ok {
WriteError(w, 4 /*User Exists*/)
} else {
WriteError(w, 999 /*Internal Error*/)
log.Print(err)
}
return
}
w.WriteHeader(201 /*Created*/)
err = user.Write(w)
if err != nil {
WriteError(w, 999 /*Internal Error*/)
log.Print(err)
return
}
} else {
user, err := GetUserFromSession(r)
if err != nil {
WriteError(w, 1 /*Not Signed In*/)
return
}
userid, err := GetURLID(r.URL.Path)
if err != nil {
WriteError(w, 3 /*Invalid Request*/)
return
}
if userid != user.UserId {
WriteError(w, 2 /*Unauthorized Access*/)
return
}
if r.Method == "GET" {
err = user.Write(w)
if err != nil {
WriteError(w, 999 /*Internal Error*/)
log.Print(err)
return
}
} else if r.Method == "PUT" {
user_json := r.PostFormValue("user")
if user_json == "" {
WriteError(w, 3 /*Invalid Request*/)
return
}
// Save old PWHash in case the new password is bogus
old_pwhash := user.PasswordHash
// Save GroupId because we don't allow changing them
groupid := user.GroupId
err = user.Read(user_json)
if err != nil || user.UserId != userid {
WriteError(w, 3 /*Invalid Request*/)
return
}
// If the user didn't create a new password, keep their old one
if user.Password != BogusPassword {
user.HashPassword()
} else {
user.Password = ""
user.PasswordHash = old_pwhash
}
user.GroupId = groupid //ensure this doesn't change
count, err := DB.Update(user)
if count != 1 || err != nil {
WriteError(w, 999 /*Internal Error*/)
log.Print(err)
return
}
err = user.Write(w)
if err != nil {
WriteError(w, 999 /*Internal Error*/)
log.Print(err)
return
}
} else if r.Method == "DELETE" {
//TODO delete everything else too?
//TODO how to handle making sure users really meant to delete everything?
count, err := DB.Delete(&user)
if count != 1 || err != nil {
WriteError(w, 999 /*Internal Error*/)
log.Print(err)
return
}
WriteSuccess(w)
}
}
}