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.
Some checks failed
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/tests Pipeline failed
ci/woodpecker/push/build unknown status

This commit is contained in:
qpismont 2025-03-12 21:36:57 +00:00
parent dcc5df8300
commit 26ce8522ac
11 changed files with 69 additions and 5 deletions

View file

@ -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
} }
} }

View file

@ -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
}

View file

@ -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)
} }

View file

@ -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)
} }

View file

@ -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)

View file

@ -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,

View file

@ -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
View 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
}

View 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))
}