2017-10-04 19:35:59 -04:00
package handlers
2015-06-25 22:36:58 -04:00
import (
"crypto/sha256"
"encoding/json"
2017-06-21 21:25:38 -04:00
"errors"
2015-06-25 22:36:58 -04:00
"fmt"
2017-06-21 21:25:38 -04:00
"gopkg.in/gorp.v1"
2015-06-25 22:36:58 -04:00
"io"
"log"
"net/http"
"strings"
)
type User struct {
2017-06-21 21:25:38 -04:00
UserId int64
DefaultCurrency int64 // SecurityId of default currency, or ISO4217 code for it if creating new user
Name string
Username string
Password string ` db:"-" `
PasswordHash string ` json:"-" `
Email string
2015-06-25 22:36:58 -04:00
}
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 = ""
}
2017-10-04 08:05:51 -04:00
func GetUser ( db * DB , userid int64 ) ( * User , error ) {
2015-06-25 22:36:58 -04:00
var u User
2017-10-04 08:05:51 -04:00
err := db . SelectOne ( & u , "SELECT * from users where UserId=?" , userid )
2015-06-25 22:36:58 -04:00
if err != nil {
return nil , err
}
return & u , nil
}
2017-06-21 21:25:38 -04:00
func GetUserTx ( transaction * gorp . Transaction , userid int64 ) ( * User , error ) {
var u User
err := transaction . SelectOne ( & u , "SELECT * from users where UserId=?" , userid )
if err != nil {
return nil , err
}
return & u , nil
}
2017-10-04 08:05:51 -04:00
func GetUserByUsername ( db * DB , username string ) ( * User , error ) {
2015-06-25 22:36:58 -04:00
var u User
2017-10-04 08:05:51 -04:00
err := db . SelectOne ( & u , "SELECT * from users where Username=?" , username )
2015-06-25 22:36:58 -04:00
if err != nil {
return nil , err
}
return & u , nil
}
2017-10-04 08:05:51 -04:00
func InsertUser ( db * DB , u * User ) error {
transaction , err := db . Begin ( )
2015-06-25 22:36:58 -04:00
if err != nil {
return err
}
2017-06-21 21:25:38 -04:00
security_template := FindCurrencyTemplate ( u . DefaultCurrency )
if security_template == nil {
transaction . Rollback ( )
return errors . New ( "Invalid ISO4217 Default Currency" )
}
2015-06-25 22:36:58 -04:00
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
}
2017-06-21 21:25:38 -04:00
// Copy the security template and give it our new UserId
var security Security
security = * security_template
security . UserId = u . UserId
err = InsertSecurityTx ( transaction , & security )
if err != nil {
transaction . Rollback ( )
return err
}
// Update the user's DefaultCurrency to our new SecurityId
u . DefaultCurrency = security . SecurityId
count , err := transaction . Update ( u )
if err != nil {
transaction . Rollback ( )
return err
} else if count != 1 {
transaction . Rollback ( )
return errors . New ( "Would have updated more than one user" )
}
2015-06-25 22:36:58 -04:00
err = transaction . Commit ( )
if err != nil {
transaction . Rollback ( )
return err
}
return nil
}
2017-10-04 08:05:51 -04:00
func GetUserFromSession ( db * DB , r * http . Request ) ( * User , error ) {
s , err := GetSession ( db , r )
2015-06-25 22:36:58 -04:00
if err != nil {
return nil , err
}
2017-10-04 08:05:51 -04:00
return GetUser ( db , s . UserId )
2015-06-25 22:36:58 -04:00
}
2017-10-04 08:05:51 -04:00
func UpdateUser ( db * DB , u * User ) error {
transaction , err := db . Begin ( )
2017-06-21 21:25:38 -04:00
if err != nil {
return err
}
security , err := GetSecurityTx ( transaction , u . DefaultCurrency , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
} else if security . UserId != u . UserId || security . SecurityId != u . DefaultCurrency {
transaction . Rollback ( )
return errors . New ( "UserId and DefaultCurrency don't match the fetched security" )
} else if security . Type != Currency {
transaction . Rollback ( )
return errors . New ( "New DefaultCurrency security is not a currency" )
}
count , err := transaction . Update ( u )
if err != nil {
transaction . Rollback ( )
return err
} else if count != 1 {
transaction . Rollback ( )
return errors . New ( "Would have updated more than one user" )
}
err = transaction . Commit ( )
if err != nil {
transaction . Rollback ( )
return err
}
return nil
}
2017-10-07 21:04:59 -04:00
func DeleteUser ( db * DB , u * User ) error {
transaction , err := db . Begin ( )
if err != nil {
return err
}
count , err := transaction . Delete ( u )
if err != nil {
transaction . Rollback ( )
return err
}
if count != 1 {
transaction . Rollback ( )
return fmt . Errorf ( "No user to delete" )
}
_ , err = transaction . Exec ( "DELETE FROM prices WHERE prices.PriceId IN (SELECT prices.PriceId FROM prices INNER JOIN securities ON prices.SecurityId=securities.SecurityId WHERE securities.UserId=?)" , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
}
_ , err = transaction . Exec ( "DELETE FROM splits WHERE splits.SplitId IN (SELECT splits.SplitId FROM splits INNER JOIN transactions ON splits.TransactionId=transactions.TransactionId WHERE transactions.UserId=?)" , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
}
_ , err = transaction . Exec ( "DELETE FROM transactions WHERE transactions.UserId=?" , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
}
_ , err = transaction . Exec ( "DELETE FROM securities WHERE securities.UserId=?" , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
}
_ , err = transaction . Exec ( "DELETE FROM accounts WHERE accounts.UserId=?" , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
}
_ , err = transaction . Exec ( "DELETE FROM reports WHERE reports.UserId=?" , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
}
_ , err = transaction . Exec ( "DELETE FROM sessions WHERE sessions.UserId=?" , u . UserId )
if err != nil {
transaction . Rollback ( )
return err
}
err = transaction . Commit ( )
if err != nil {
transaction . Rollback ( )
return err
}
return nil
}
2017-10-04 08:05:51 -04:00
func UserHandler ( w http . ResponseWriter , r * http . Request , db * DB ) {
2015-06-25 22:36:58 -04:00
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 ( )
2017-10-04 08:05:51 -04:00
err = InsertUser ( db , & user )
2015-06-25 22:36:58 -04:00
if err != nil {
if _ , ok := err . ( UserExistsError ) ; ok {
WriteError ( w , 4 /*User Exists*/ )
} else {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
}
return
}
2016-10-04 08:01:28 -04:00
w . WriteHeader ( 201 /*Created*/ )
err = user . Write ( w )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
2015-06-25 22:36:58 -04:00
} else {
2017-10-04 08:05:51 -04:00
user , err := GetUserFromSession ( db , r )
2015-06-25 22:36:58 -04:00
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
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
}
2017-10-04 08:05:51 -04:00
err = UpdateUser ( db , user )
2017-06-21 21:25:38 -04:00
if err != nil {
2015-06-25 22:36:58 -04:00
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
2016-10-04 08:01:28 -04:00
err = user . Write ( w )
if err != nil {
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
2015-06-25 22:36:58 -04:00
} else if r . Method == "DELETE" {
2017-10-07 21:04:59 -04:00
err := DeleteUser ( db , user )
if err != nil {
2015-06-25 22:36:58 -04:00
WriteError ( w , 999 /*Internal Error*/ )
log . Print ( err )
return
}
WriteSuccess ( w )
}
}
}