Refactor account handling: update Login method signature, introduce AccountLogin and AccountCreate types, modify repository interface, and implement login logic in service. Add environment variable loading and hashing functions with tests.
This commit is contained in:
parent
dcc5df8300
commit
26ce8522ac
11 changed files with 69 additions and 5 deletions
|
@ -19,7 +19,7 @@ func NewController(service domain.AccountService) Controller {
|
||||||
return Controller{service: service}
|
return Controller{service: service}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) Login(w *core.Response, r *http.Request) {
|
func (c Controller) Login(w *core.Response, r *http.Request) {
|
||||||
var request LoginAccountRequest
|
var request LoginAccountRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||||
w.WriteError(core.ErrInvalidStruct)
|
w.WriteError(core.ErrInvalidStruct)
|
||||||
|
@ -30,4 +30,5 @@ func (c *Controller) Login(w *core.Response, r *http.Request) {
|
||||||
w.WriteError(core.ErrInvalidStruct)
|
w.WriteError(core.ErrInvalidStruct)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,3 +8,14 @@ type Account struct {
|
||||||
CreatedAt string `db:"created_at" json:"created_at"`
|
CreatedAt string `db:"created_at" json:"created_at"`
|
||||||
UpdatedAt string `db:"updated_at" json:"updated_at"`
|
UpdatedAt string `db:"updated_at" json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccountLogin struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountCreate struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
RoleId int
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
type AccountRepository interface {
|
type AccountRepository interface {
|
||||||
Insert(account *Account) (int, error)
|
Insert(account Account) (int, error)
|
||||||
FetchOneByUsername(username string) (*Account, error)
|
FetchOneByUsername(username string) (*Account, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
import "gitea.qpismont.fr/qpismont/trepa/internal/core"
|
||||||
|
|
||||||
type AccountService interface {
|
type AccountService interface {
|
||||||
|
Login(login AccountLogin) (*Account, *core.HTTPError)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ func NewRepository(db *sqlx.DB) domain.AccountRepository {
|
||||||
return &Repository{db: db}
|
return &Repository{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) Insert(account *domain.Account) (int, error) {
|
func (r *Repository) Insert(account domain.Account) (int, error) {
|
||||||
var id int
|
var id int
|
||||||
|
|
||||||
stmt, err := r.db.Prepare(SqlInsert)
|
stmt, err := r.db.Prepare(SqlInsert)
|
||||||
|
|
|
@ -14,7 +14,7 @@ func TestRepository_Insert(t *testing.T) {
|
||||||
|
|
||||||
repo := NewRepository(db)
|
repo := NewRepository(db)
|
||||||
|
|
||||||
account := &domain.Account{
|
account := domain.Account{
|
||||||
Username: "test",
|
Username: "test",
|
||||||
Password: "test",
|
Password: "test",
|
||||||
RoleId: 1,
|
RoleId: 1,
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import "gitea.qpismont.fr/qpismont/trepa/internal/accounts/domain"
|
import (
|
||||||
|
"gitea.qpismont.fr/qpismont/trepa/internal/accounts/domain"
|
||||||
|
"gitea.qpismont.fr/qpismont/trepa/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
repository domain.AccountRepository
|
repository domain.AccountRepository
|
||||||
|
@ -9,3 +12,16 @@ type Service struct {
|
||||||
func NewService(repository domain.AccountRepository) domain.AccountService {
|
func NewService(repository domain.AccountRepository) domain.AccountService {
|
||||||
return &Service{repository: repository}
|
return &Service{repository: repository}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) Login(login domain.AccountLogin) (*domain.Account, *core.HTTPError) {
|
||||||
|
account, err := s.repository.FetchOneByUsername(login.Username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, domain.ErrAccountNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if !core.ComparePassword(login.Password, account.Password) {
|
||||||
|
return nil, domain.ErrBadPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
return account, nil
|
||||||
|
}
|
||||||
|
|
12
internal/core/hash.go
Normal file
12
internal/core/hash.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import "golang.org/x/crypto/argon2"
|
||||||
|
|
||||||
|
func HashPassword(password string) string {
|
||||||
|
return string(argon2.IDKey([]byte(password), nil, 1, 64*1024, 4, 32))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ComparePassword(password string, hash string) bool {
|
||||||
|
hashedPassword := HashPassword(password)
|
||||||
|
return hashedPassword == hash
|
||||||
|
}
|
21
internal/core/hash_test.go
Normal file
21
internal/core/hash_test.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHashPassword(t *testing.T) {
|
||||||
|
password := "password"
|
||||||
|
hashedPassword := HashPassword(password)
|
||||||
|
assert.NotEmpty(t, hashedPassword)
|
||||||
|
t.Log(hashedPassword)
|
||||||
|
assert.Equal(t, hashedPassword, "LOLPASSWORD")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComparePassword(t *testing.T) {
|
||||||
|
password := "password"
|
||||||
|
hashedPassword := HashPassword(password)
|
||||||
|
assert.True(t, ComparePassword(password, hashedPassword))
|
||||||
|
}
|
Loading…
Reference in a new issue