From d05ea66f4bbcf0cc5c8908f3435c68de1b070fa1 Mon Sep 17 00:00:00 2001 From: Alexander Neonxp Kiryukhin Date: Sat, 12 Oct 2024 02:52:22 +0300 Subject: Начальная версия MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/service/user/login.go | 31 ++++++++++++++++ pkg/service/user/register.go | 85 ++++++++++++++++++++++++++++++++++++++++++++ pkg/service/user/user.go | 12 +++++++ 3 files changed, 128 insertions(+) create mode 100644 pkg/service/user/login.go create mode 100644 pkg/service/user/register.go create mode 100644 pkg/service/user/user.go (limited to 'pkg/service/user') diff --git a/pkg/service/user/login.go b/pkg/service/user/login.go new file mode 100644 index 0000000..d1f5c65 --- /dev/null +++ b/pkg/service/user/login.go @@ -0,0 +1,31 @@ +package user + +import ( + "context" + "errors" + + normalizer "github.com/dimuska139/go-email-normalizer/v3" + "golang.org/x/crypto/bcrypt" + + "go.neonxp.ru/framework/pkg/model" + "go.neonxp.ru/framework/pkg/tpl" +) + +var ErrInvalidUserOrPassword = errors.New("invalid_user_or_password") + +func (s *Service) Login(ctx context.Context, form *tpl.LoginForm) (*model.User, error) { + n := normalizer.NewNormalizer() + u := &model.User{ + Email: n.Normalize(form.Email), + } + + if err := s.db.NewSelect().Model(u).Where("email = ?", u.Email).Scan(ctx, u); err != nil { + return nil, errors.Join(err, ErrInvalidUserOrPassword) + } + + if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(form.Password)); err != nil { + return nil, errors.Join(err, ErrInvalidUserOrPassword) + } + + return u, nil +} diff --git a/pkg/service/user/register.go b/pkg/service/user/register.go new file mode 100644 index 0000000..69f19a2 --- /dev/null +++ b/pkg/service/user/register.go @@ -0,0 +1,85 @@ +package user + +import ( + "context" + "errors" + "regexp" + + normalizer "github.com/dimuska139/go-email-normalizer/v3" + "github.com/uptrace/bun/driver/pgdriver" + "golang.org/x/crypto/bcrypt" + + "go.neonxp.ru/framework/pkg/model" + "go.neonxp.ru/framework/pkg/tpl" +) + +var ( + ErrUsernameToShort = errors.New("username_to_short") + ErrUserAlreadyExist = errors.New("user_already_exists") + ErrPasswordTooWeak = errors.New("password_too_weak") + ErrPasswordTooShort = errors.New("password_too_short") +) + +const ( + minUsernameLen = 3 + minPasswordLen = 8 +) + +func (s *Service) Register(ctx context.Context, form *tpl.RegisterForm) (*model.User, error) { + if len(form.Username) < minUsernameLen { + return nil, ErrUsernameToShort + } + + if err := checkPasswordLever(form.Password); err != nil { + return nil, err + } + + password, err := bcrypt.GenerateFromPassword([]byte(form.Password), bcrypt.DefaultCost) + if err != nil { + return nil, err + } + + n := normalizer.NewNormalizer() + u := &model.User{ + Username: form.Username, + Email: n.Normalize(form.Email), + Password: string(password), + } + + if _, err := s.db.NewInsert().Model(u).Returning("*").Exec(ctx); err != nil { + pqErr := pgdriver.Error{} + if errors.As(err, &pqErr) { + if pqErr.Field('C') == "23505" { + return nil, ErrUserAlreadyExist + } + } + + return nil, err + } + + return u, nil +} + +func checkPasswordLever(ps string) error { + if len(ps) < minPasswordLen { + return ErrPasswordTooShort + } + + lowerCase := `[a-z]{1}` + upperCase := `[A-Z]{1}` + symbol := `[0-9!@#~$%^&*()+|_]{1}` + + if b, err := regexp.MatchString(lowerCase, ps); !b || err != nil { + return ErrPasswordTooWeak + } + + if b, err := regexp.MatchString(upperCase, ps); !b || err != nil { + return ErrPasswordTooWeak + } + + if b, err := regexp.MatchString(symbol, ps); !b || err != nil { + return ErrPasswordTooWeak + } + + return nil +} diff --git a/pkg/service/user/user.go b/pkg/service/user/user.go new file mode 100644 index 0000000..aa27eee --- /dev/null +++ b/pkg/service/user/user.go @@ -0,0 +1,12 @@ +package user + +import "github.com/uptrace/bun" + +type Service struct { + db *bun.DB +} + +// NewService returns new Service. +func NewService(db *bun.DB) *Service { + return &Service{db: db} +} -- cgit v1.2.3