diff --git a/src/domain/repository/interface.go b/src/domain/repository/interface.go new file mode 100644 index 0000000..48684b9 --- /dev/null +++ b/src/domain/repository/interface.go @@ -0,0 +1,10 @@ +package repository + +import entity "git.ego.freeddns.org/egommerce/api-entities/identity/entity" + +type UserRepositoryInterface interface { + GetByID(id string) (*entity.User, error) + Create(user *entity.User) (*entity.User, error) + Update(user *entity.User) (*entity.User, error) + Delete(id int64) (bool, error) +} diff --git a/src/domain/repository/user_repository.go b/src/domain/repository/user_repository.go new file mode 100644 index 0000000..0016196 --- /dev/null +++ b/src/domain/repository/user_repository.go @@ -0,0 +1,47 @@ +package repository + +import ( + "context" + "errors" + + entity "git.ego.freeddns.org/egommerce/api-entities/identity/entity" + db "git.ego.freeddns.org/egommerce/go-api-pkg/database" + + "github.com/jackc/pgx/v5/pgxpool" +) + +type UserRepository struct { + db *pgxpool.Pool +} + +func NewUserRepository(db *pgxpool.Pool) *UserRepository { + return &UserRepository{db} +} + +func (r *UserRepository) GetByID(id string) (*entity.User, error) { + return &entity.User{}, nil +} + +func (r *UserRepository) Create(user *entity.User) (string, error) { + var id string + + sql := `INSERT INTO identity.users(email, username, password) VALUES($1, $2, $3) LIMIT 1 RETURNING id` + err := r.db.QueryRow(context.Background(), sql, user.Email, user.Username, user.Password).Scan(&id) + if err != nil { + if err = db.IsDuplicatedRow(err); err != nil { + return "", errors.New("username/email is already taken") + } + + return "", errors.New("db error: " + err.Error()) + } + + return id, nil +} + +func (r *UserRepository) Update(user *entity.User) (*entity.User, error) { + return &entity.User{}, nil +} + +func (r *UserRepository) Delete(id int64) (bool, error) { + return true, nil +} diff --git a/src/internal/server/login_handler.go b/src/internal/server/login_handler.go index fc4ec83..92dc107 100644 --- a/src/internal/server/login_handler.go +++ b/src/internal/server/login_handler.go @@ -2,6 +2,8 @@ package server import ( dto "git.ego.freeddns.org/egommerce/api-entities/identity/dto" + + domain "git.ego.freeddns.org/egommerce/identity-service/domain/repository" "git.ego.freeddns.org/egommerce/identity-service/internal/service" "github.com/gofiber/fiber/v2" @@ -13,7 +15,8 @@ func (s *Server) LoginHandlerFn(c *fiber.Ctx) error { return s.Error(c, fiber.StatusBadRequest, "Error parsing input") } - authSrv := service.NewAuthService(s.GetDatabase(), s.GetCache()) + repo := domain.NewUserRepository(s.GetDatabase()) + authSrv := service.NewAuthService(repo, s.GetDatabase(), s.GetCache()) token, err := authSrv.Login(data.Username, data.Password) if err != nil { diff --git a/src/internal/server/refresh_handler.go b/src/internal/server/refresh_handler.go index 0d2408b..25add8e 100644 --- a/src/internal/server/refresh_handler.go +++ b/src/internal/server/refresh_handler.go @@ -2,6 +2,7 @@ package server import ( dto "git.ego.freeddns.org/egommerce/api-entities/identity/dto" + domain "git.ego.freeddns.org/egommerce/identity-service/domain/repository" "git.ego.freeddns.org/egommerce/identity-service/internal/service" "github.com/gofiber/fiber/v2" @@ -13,7 +14,8 @@ func (s *Server) RefreshHandlerFn(c *fiber.Ctx) error { return s.Error(c, fiber.StatusBadRequest, "Error parsing input") } - authSrv := service.NewAuthService(s.GetDatabase(), s.GetCache()) + repo := domain.NewUserRepository(s.GetDatabase()) + authSrv := service.NewAuthService(repo, s.GetDatabase(), s.GetCache()) token, err := authSrv.RefreshToken(data.AccessToken) if err != nil { diff --git a/src/internal/server/register_handler.go b/src/internal/server/register_handler.go index 86cfdc4..c4bfd6e 100644 --- a/src/internal/server/register_handler.go +++ b/src/internal/server/register_handler.go @@ -2,6 +2,7 @@ package server import ( dto "git.ego.freeddns.org/egommerce/api-entities/identity/dto" + domain "git.ego.freeddns.org/egommerce/identity-service/domain/repository" "git.ego.freeddns.org/egommerce/identity-service/internal/service" "github.com/gofiber/fiber/v2" @@ -13,7 +14,8 @@ func (s *Server) RegisterHandlerFn(c *fiber.Ctx) error { return s.Error(c, fiber.StatusBadRequest, "Error parsing input") } - authSrv := service.NewAuthService(s.GetDatabase(), s.GetCache()) + repo := domain.NewUserRepository(s.GetDatabase()) + authSrv := service.NewAuthService(repo, s.GetDatabase(), s.GetCache()) id, err := authSrv.Register(data.Email, data.Username, data.Password) if err != nil { diff --git a/src/internal/service/auth.go b/src/internal/service/auth.go index a3949e1..d93918f 100644 --- a/src/internal/service/auth.go +++ b/src/internal/service/auth.go @@ -5,14 +5,15 @@ import ( "errors" "fmt" - db "git.ego.freeddns.org/egommerce/identity-service/pkg/database" + entity "git.ego.freeddns.org/egommerce/api-entities/identity/entity" + domain "git.ego.freeddns.org/egommerce/identity-service/domain/repository" "github.com/go-redis/redis/v8" "github.com/jackc/pgx/v5/pgxpool" ) var ( - AuthService *Auth + passSrv *PaswordService ErrLoginIncorrect = errors.New("login incorrect") ErrUnableToCacheToken = errors.New("unable to save token in cache") @@ -20,33 +21,40 @@ var ( ) func init() { + passSrv = NewPasswordService() } type Auth struct { + repo *domain.UserRepository db *pgxpool.Pool cache *redis.Client } -func NewAuthService(db *pgxpool.Pool, cache *redis.Client) *Auth { +func NewAuthService(repo *domain.UserRepository, db *pgxpool.Pool, cache *redis.Client) *Auth { return &Auth{ + repo: repo, db: db, cache: cache, } } func (a *Auth) Login(login, passwd string) (string, error) { - var id string + var id, hashedPasswd string - sql := `SELECT id FROM identity.users WHERE username=$1 AND password=$2 LIMIT 1` - err := a.db.QueryRow(context.Background(), sql, login, passwd).Scan(&id) + sql := `SELECT id, password FROM identity.users WHERE username=$1 LIMIT 1` + err := a.db.QueryRow(context.Background(), sql, login).Scan(&id, &hashedPasswd) if err != nil { - // if err = db.NoRowsInQuerySet(err); err != nil { // FIXME NoRowsInQuerySet error detect + // if err = database.NoRowsInQuerySet(err); err != nil { // return "", errors.New("no user found") // } return "", ErrLoginIncorrect } + if err = passSrv.Verify(passwd, hashedPasswd); err != nil { + return "", ErrLoginIncorrect + } + accessToken, _ := jwtSrv.CreateAccessToken(id) refreshToken, _ := jwtSrv.CreateRefreshToken(id) if err = a.saveTokensToCache(id, accessToken, refreshToken); err != nil { @@ -74,16 +82,15 @@ func (a *Auth) RefreshToken(accessToken string) (string, error) { } func (a *Auth) Register(email, login, passwd string) (string, error) { - var id string + passwd, _ = passSrv.Hash(passwd) - sql := `INSERT INTO identity.users(email, username, password) VALUES($1, $2, $3) LIMIT 1 RETURNING id` - err := a.db.QueryRow(context.Background(), sql, email, login, passwd).Scan(&id) + id, err := a.repo.Create(&entity.User{ + Email: email, + Username: login, + Password: passwd, + }) if err != nil { - if err = db.IsDuplicatedRow(err); err != nil { - return "", errors.New("username/email is already taken") - } - - return "", errors.New("Failed to create new user: " + err.Error()) + return "", err } return id, nil diff --git a/src/internal/service/password.go b/src/internal/service/password.go new file mode 100644 index 0000000..f24b935 --- /dev/null +++ b/src/internal/service/password.go @@ -0,0 +1,22 @@ +package service + +import "golang.org/x/crypto/bcrypt" + +type PaswordService struct{} + +func NewPasswordService() *PaswordService { + return &PaswordService{} +} + +func (p *PaswordService) Hash(pass string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost) + if err != nil { + return "", err + } + + return string(hash), nil +} + +func (p *PaswordService) Verify(pass, hashedPass string) error { + return bcrypt.CompareHashAndPassword([]byte(hashedPass), []byte(pass)) +}