newsbot-api/internal/repository/users.go

165 lines
3.7 KiB
Go
Raw Permalink Normal View History

package repository
import (
"database/sql"
"errors"
"fmt"
"time"
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
"github.com/huandu/go-sqlbuilder"
"golang.org/x/crypto/bcrypt"
)
const (
TableName string = "users"
ErrUserNotFound string = "requested user was not found"
)
type Users interface {
GetByName(name string) (domain.UserEntity, error)
Create(name, password, scope string) (int64, error)
Update(id int, entity domain.UserEntity) error
UpdatePassword(name, password string) error
CheckUserHash(name, password string) error
UpdateScopes(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, scope 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", "UpdatedAt", "CreatedAt", "Scopes")
queryBuilder.Values(name, string(hash), dt, dt, scope)
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) UpdateScopes(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 int64
var username string
var hash string
var createdAt time.Time
var updatedAt time.Time
var deletedAt sql.NullTime
var scopes string
err := rows.Scan(&id, &createdAt, &updatedAt, &deletedAt, &username, &hash, &scopes)
if err != nil {
fmt.Println(err)
}
item := domain.UserEntity{
ID: id,
UpdatedAt: updatedAt,
Username: username,
Hash: hash,
Scopes: scopes,
CreatedAt: createdAt,
}
if deletedAt.Valid {
item.DeletedAt = deletedAt.Time
}
items = append(items, item)
}
return items
}