go-cook/api/repositories/users.go

158 lines
3.5 KiB
Go

package repositories
import (
"database/sql"
"errors"
"fmt"
"go-cook/api/domain"
"time"
"github.com/huandu/go-sqlbuilder"
"golang.org/x/crypto/bcrypt"
)
const (
TableName string = "users"
ErrUserNotFound string = "requested user was not found"
)
type IUserTable interface {
GetByName(name string) (domain.UserEntity, error)
Create(name, password string) (int64, error)
Update(id int, entity domain.UserEntity) error
UpdatePassword(name, password string) error
CheckUserHash(name, password string) error
AddScope(name, scope string) error
}
// Creates a new instance of UserRepository with the bound sql
func NewUserRepository(conn *sql.DB) UserRepository {
return UserRepository{
connection: conn,
}
}
type UserRepository struct {
connection *sql.DB
}
func (ur UserRepository) GetByName(name string) (domain.UserEntity, error) {
builder := sqlbuilder.NewSelectBuilder()
builder.Select("*").From("users").Where(
builder.E("Name", name),
)
query, args := builder.Build()
rows, err := ur.connection.Query(query, args...)
if err != nil {
return domain.UserEntity{}, err
}
data := ur.processRows(rows)
if len(data) == 0 {
return domain.UserEntity{}, errors.New(ErrUserNotFound)
}
return data[0], nil
}
func (ur UserRepository) Create(name, password string) (int64, error) {
passwordBytes := []byte(password)
hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
if err != nil {
return 0, err
}
dt := time.Now()
queryBuilder := sqlbuilder.NewInsertBuilder()
queryBuilder.InsertInto("users")
queryBuilder.Cols("Name", "Hash", "LastUpdated", "CreatedAt")
queryBuilder.Values(name, string(hash), dt, dt)
query, args := queryBuilder.Build()
_, err = ur.connection.Exec(query, args...)
if err != nil {
return 0, err
}
return 1, nil
}
func (ur UserRepository) Update(id int, entity domain.UserEntity) error {
return errors.New("not implemented")
}
func (ur UserRepository) UpdatePassword(name, password string) error {
_, err := ur.GetByName(name)
if err != nil {
return nil
}
queryBuilder := sqlbuilder.NewUpdateBuilder()
queryBuilder.Update(TableName)
//queryBuilder.Set
return nil
}
// If the hash matches what we have in the database, an error will not be returned.
// If the user does not exist or the hash does not match, an error will be returned
func (ur UserRepository) CheckUserHash(name, password string) error {
record, err := ur.GetByName(name)
if err != nil {
return err
}
err = bcrypt.CompareHashAndPassword([]byte(record.Hash), []byte(password))
if err != nil {
return err
}
return nil
}
func (ur UserRepository) AddScope(name, scope string) error {
builder := sqlbuilder.NewUpdateBuilder()
builder.Update("users")
builder.Set (
builder.Assign("Scopes", scope),
)
builder.Where(
builder.Equal("Name", name),
)
query, args := builder.Build()
_, err := ur.connection.Exec(query, args...)
if err != nil {
return err
}
return nil
}
func (ur UserRepository) processRows(rows *sql.Rows) []domain.UserEntity {
items := []domain.UserEntity{}
for rows.Next() {
var id int
var name string
var hash string
var createdAt time.Time
var lastUpdated time.Time
var scopes string
err := rows.Scan(&id, &name, &hash, &createdAt, &lastUpdated, &scopes)
if err != nil {
fmt.Println(err)
}
items = append(items, domain.UserEntity{
Id: id,
Name: name,
Hash: hash,
Scopes: scopes,
CreatedAt: createdAt,
LastUpdated: lastUpdated,
})
}
return items
}