This repository has been archived on 2025-03-30. You can view files and clone it, but cannot push or open issues or pull requests.
bactdb/users.go
2015-06-24 22:37:47 -08:00

182 lines
4.4 KiB
Go

package main
import (
"database/sql"
"encoding/json"
"errors"
"net/http"
"net/url"
"golang.org/x/crypto/bcrypt"
)
var (
ErrUserNotFound = errors.New("User not found")
ErrUserNotFoundJSON = newJSONError(ErrUserNotFound, http.StatusNotFound)
ErrUserNotUpdated = errors.New("User not updated")
ErrUserNotUpdatedJSON = newJSONError(ErrUserNotUpdated, http.StatusBadRequest)
ErrInvalidEmailOrPassword = errors.New("Invalid email or password")
)
func init() {
DB.AddTableWithName(User{}, "users").SetKeys(true, "Id")
}
type UserService struct{}
type User struct {
Id int64 `json:"id,omitempty"`
Email string `db:"email" json:"email"`
Password string `db:"password" json:"-"`
Name string `db:"name" json:"name"`
Role string `db:"role" json:"role"`
CreatedAt NullTime `db:"created_at" json:"createdAt"`
UpdatedAt NullTime `db:"updated_at" json:"updatedAt"`
DeletedAt NullTime `db:"deleted_at" json:"deletedAt"`
}
type UserValidation struct {
Email []string `json:"email,omitempty"`
Password []string `json:"password,omitempty"`
Name []string `json:"name,omitempty"`
Role []string `json:"role,omitempty"`
}
func (uv UserValidation) Error() string {
errs, err := json.Marshal(struct {
UserValidation `json:"errors"`
}{uv})
if err != nil {
return err.Error()
}
return string(errs)
}
type Users []*User
type UserJSON struct {
User *User `json:"user"`
}
type UsersJSON struct {
Users *Users `json:"users"`
}
func (u *User) marshal() ([]byte, error) {
return json.Marshal(&UserJSON{User: u})
}
func (u *Users) marshal() ([]byte, error) {
return json.Marshal(&UsersJSON{Users: u})
}
func (u UserService) unmarshal(b []byte) (entity, error) {
var uj UserJSON
err := json.Unmarshal(b, &uj)
return uj.User, err
}
func (u *User) validate() error {
var uv UserValidation
validationError := false
if u.Name == "" {
uv.Name = append(uv.Name, "Must provide a value")
validationError = true
}
if validationError {
return uv
}
return nil
}
func (u UserService) list(val *url.Values) (entity, *appError) {
if val == nil {
return nil, ErrMustProvideOptionsJSON
}
var opt ListOptions
if err := schemaDecoder.Decode(&opt, *val); err != nil {
return nil, newJSONError(err, http.StatusInternalServerError)
}
users := make(Users, 0)
sql := `SELECT * FROM users;`
if err := DBH.Select(&users, sql); err != nil {
return nil, newJSONError(err, http.StatusInternalServerError)
}
return &users, nil
}
func (u UserService) get(id int64, genus string) (entity, *appError) {
var user User
q := `SELECT * FROM users WHERE id=$1;`
if err := DBH.SelectOne(&user, q, id); err != nil {
if err == sql.ErrNoRows {
return nil, ErrUserNotFoundJSON
}
return nil, newJSONError(err, http.StatusInternalServerError)
}
return &user, nil
}
func (u UserService) update(id int64, e *entity, claims Claims) *appError {
user := (*e).(*User)
user.UpdatedAt = currentTime()
user.Id = id
count, err := DBH.Update(user)
if err != nil {
return newJSONError(err, http.StatusInternalServerError)
}
if count != 1 {
return ErrUserNotUpdatedJSON
}
return nil
}
func (u UserService) create(e *entity, claims Claims) *appError {
user := (*e).(*User)
if err := user.validate(); err != nil {
return &appError{Error: err, Status: StatusUnprocessableEntity}
}
ct := currentTime()
user.CreatedAt = ct
user.UpdatedAt = ct
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 10)
if err != nil {
return newJSONError(err, http.StatusInternalServerError)
}
user.Password = string(hash)
if err := DBH.Insert(user); err != nil {
return newJSONError(err, http.StatusInternalServerError)
}
return nil
}
// for thermokarst/jwt: authentication callback
func dbAuthenticate(email string, password string) error {
var user User
q := `SELECT * FROM users WHERE lower(email)=lower($1);`
if err := DBH.SelectOne(&user, q, email); err != nil {
return ErrInvalidEmailOrPassword
}
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
return ErrInvalidEmailOrPassword
}
return nil
}
// for thermokarst/jwt: setting user in claims bundle
func dbGetUserByEmail(email string) (*User, error) {
var user User
q := `SELECT * FROM users WHERE lower(email)=lower($1);`
if err := DBH.SelectOne(&user, q, email); err != nil {
if err == sql.ErrNoRows {
return nil, ErrUserNotFound
}
return nil, err
}
return &user, nil
}