add confirmation codes
This commit is contained in:
parent
edaaffeb61
commit
59e76a4ec1
@ -8,14 +8,14 @@ import (
|
||||
)
|
||||
|
||||
type createUserInput struct {
|
||||
Login string
|
||||
Email string
|
||||
Password string
|
||||
Name string
|
||||
}
|
||||
|
||||
type createUserOutput struct {
|
||||
Id string `json:"id"`
|
||||
Login string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ func NewUserCreateHandler(userService services.UserService) gin.HandlerFunc {
|
||||
}
|
||||
|
||||
dto, err := userService.CreateUser(ctx, services.UserCreateParams{
|
||||
Login: params.Login,
|
||||
Email: params.Email,
|
||||
Password: params.Password,
|
||||
Name: params.Name,
|
||||
})
|
||||
@ -43,7 +43,7 @@ func NewUserCreateHandler(userService services.UserService) gin.HandlerFunc {
|
||||
|
||||
resultBody, err := json.Marshal(createUserOutput{
|
||||
Id: dto.Id,
|
||||
Login: dto.Login,
|
||||
Email: dto.Email,
|
||||
Name: dto.Name,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
15
src/models/action_token.go
Normal file
15
src/models/action_token.go
Normal file
@ -0,0 +1,15 @@
|
||||
package models
|
||||
|
||||
type ActionTokenTarget int
|
||||
|
||||
const (
|
||||
ActionTokenTargetForgotPassword ActionTokenTarget = iota
|
||||
ActionTokenTargetLogin2FA
|
||||
)
|
||||
|
||||
type ActionTokenDTO struct {
|
||||
Id string
|
||||
UserId string
|
||||
Value string
|
||||
Target ActionTokenTarget
|
||||
}
|
||||
@ -2,7 +2,12 @@ package models
|
||||
|
||||
type UserDTO struct {
|
||||
Id string
|
||||
Login string
|
||||
Email string
|
||||
Secret string
|
||||
Name string
|
||||
}
|
||||
|
||||
type UserUpdateDTO struct {
|
||||
Secret string
|
||||
Name string
|
||||
}
|
||||
|
||||
9
src/repo/action_token.go
Normal file
9
src/repo/action_token.go
Normal file
@ -0,0 +1,9 @@
|
||||
package repo
|
||||
|
||||
import "backend/src/models"
|
||||
|
||||
type ActionTokenRepo interface {
|
||||
CreateActionToken(actionToken models.ActionTokenDTO) (*models.ActionTokenDTO, error)
|
||||
FindActionToken(userId, val string, target models.ActionTokenTarget) (*models.ActionTokenDTO, error)
|
||||
DeleteActionToken(id string) error
|
||||
}
|
||||
5
src/repo/email_repo.go
Normal file
5
src/repo/email_repo.go
Normal file
@ -0,0 +1,5 @@
|
||||
package repo
|
||||
|
||||
type EmailRepo interface {
|
||||
SendEmailForgotPassword(email, token string)
|
||||
}
|
||||
@ -16,8 +16,9 @@ import (
|
||||
|
||||
type UserRepo interface {
|
||||
CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error)
|
||||
UpdateUser(ctx context.Context, userId string, dto models.UserUpdateDTO) error
|
||||
GetUserById(ctx context.Context, id string) (*models.UserDTO, error)
|
||||
GetUserByLogin(ctx context.Context, login string) (*models.UserDTO, error)
|
||||
GetUserByEmail(ctx context.Context, login string) (*models.UserDTO, error)
|
||||
}
|
||||
|
||||
func NewUserRepo(db *sql.DB) UserRepo {
|
||||
@ -29,8 +30,8 @@ type userRepo struct {
|
||||
}
|
||||
|
||||
func (u *userRepo) CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error) {
|
||||
query := `insert into users (login, secret, name) values ($1, $2, $3) returning id;`
|
||||
row := u.db.QueryRowContext(ctx, query, dto.Login, dto.Secret, dto.Name)
|
||||
query := `insert into users (email, secret, name) values ($1, $2, $3) returning id;`
|
||||
row := u.db.QueryRowContext(ctx, query, dto.Email, dto.Secret, dto.Name)
|
||||
|
||||
id := ""
|
||||
if err := row.Scan(&id); err != nil {
|
||||
@ -39,18 +40,28 @@ func (u *userRepo) CreateUser(ctx context.Context, dto models.UserDTO) (*models.
|
||||
|
||||
return &models.UserDTO{
|
||||
Id: id,
|
||||
Login: dto.Login,
|
||||
Email: dto.Email,
|
||||
Secret: dto.Secret,
|
||||
Name: dto.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *userRepo) UpdateUser(ctx context.Context, userId string, dto models.UserUpdateDTO) error {
|
||||
query := `update users set secret=$1, name=$2 where id = $3;`
|
||||
_, err := u.db.ExecContext(ctx, query, dto.Secret, dto.Name, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userRepo) GetUserById(ctx context.Context, id string) (*models.UserDTO, error) {
|
||||
query := `select id, login, secret, name from users where id = $1;`
|
||||
query := `select id, email, secret, name from users where id = $1;`
|
||||
row := u.db.QueryRowContext(ctx, query, id)
|
||||
|
||||
dto := &models.UserDTO{}
|
||||
err := row.Scan(&dto.Id, &dto.Login, &dto.Secret, &dto.Name)
|
||||
err := row.Scan(&dto.Id, &dto.Email, &dto.Secret, &dto.Name)
|
||||
if err == nil {
|
||||
return dto, nil
|
||||
}
|
||||
@ -61,12 +72,12 @@ func (u *userRepo) GetUserById(ctx context.Context, id string) (*models.UserDTO,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (u *userRepo) GetUserByLogin(ctx context.Context, login string) (*models.UserDTO, error) {
|
||||
query := `select id, login, secret, name from users where login = $1;`
|
||||
func (u *userRepo) GetUserByEmail(ctx context.Context, login string) (*models.UserDTO, error) {
|
||||
query := `select id, email, secret, name from users where email = $1;`
|
||||
row := u.db.QueryRowContext(ctx, query, login)
|
||||
|
||||
dto := &models.UserDTO{}
|
||||
err := row.Scan(&dto.Id, &dto.Login, &dto.Secret, &dto.Name)
|
||||
err := row.Scan(&dto.Id, &dto.Email, &dto.Secret, &dto.Name)
|
||||
if err == nil {
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import (
|
||||
"backend/src/utils"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -32,6 +34,8 @@ type UserServiceDeps struct {
|
||||
Password utils.PasswordUtil
|
||||
UserRepo repo.UserRepo
|
||||
UserCache repo.Cache[string, models.UserDTO]
|
||||
EmailRepo repo.EmailRepo
|
||||
ActionTokenRepo repo.ActionTokenRepo
|
||||
}
|
||||
|
||||
type userService struct {
|
||||
@ -39,13 +43,13 @@ type userService struct {
|
||||
}
|
||||
|
||||
type UserCreateParams struct {
|
||||
Login string
|
||||
Email string
|
||||
Password string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (*models.UserDTO, error) {
|
||||
exisitngUser, err := u.deps.UserRepo.GetUserByLogin(ctx, params.Login)
|
||||
exisitngUser, err := u.deps.UserRepo.GetUserByEmail(ctx, params.Email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -63,7 +67,7 @@ func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (
|
||||
}
|
||||
|
||||
user := models.UserDTO{
|
||||
Login: params.Login,
|
||||
Email: params.Email,
|
||||
Secret: string(secret),
|
||||
Name: params.Name,
|
||||
}
|
||||
@ -78,8 +82,8 @@ func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (u *userService) AuthenticateUser(ctx context.Context, login, password string) (string, error) {
|
||||
user, err := u.deps.UserRepo.GetUserByLogin(ctx, login)
|
||||
func (u *userService) AuthenticateUser(ctx context.Context, email, password string) (string, error) {
|
||||
user, err := u.deps.UserRepo.GetUserByEmail(ctx, email)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -102,6 +106,77 @@ func (u *userService) AuthenticateUser(ctx context.Context, login, password stri
|
||||
return jwt, nil
|
||||
}
|
||||
|
||||
func (u *userService) HelpPasswordForgot(ctx context.Context, userId string) error {
|
||||
user, err := u.deps.UserRepo.GetUserById(ctx, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actionToken, err := u.deps.ActionTokenRepo.CreateActionToken(models.ActionTokenDTO{
|
||||
UserId: user.Id,
|
||||
Value: uuid.New().String(),
|
||||
Target: models.ActionTokenTargetForgotPassword,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.deps.EmailRepo.SendEmailForgotPassword(user.Email, actionToken.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userService) ChangePasswordForgot(ctx context.Context, userId, newPassword, accessCode string) error {
|
||||
user, err := u.deps.UserRepo.GetUserById(ctx, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
code, err := u.deps.ActionTokenRepo.FindActionToken(userId, accessCode, models.ActionTokenTargetForgotPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if code == nil {
|
||||
return fmt.Errorf("wrong user access code")
|
||||
}
|
||||
|
||||
if err := u.deps.ActionTokenRepo.DeleteActionToken(code.Id); err != nil {
|
||||
return fmt.Errorf("internal error occured: %w", err)
|
||||
}
|
||||
|
||||
return u.updatePassword(ctx, *user, newPassword)
|
||||
}
|
||||
|
||||
func (u *userService) ChangePassword(ctx context.Context, userId, oldPassword, newPassword string) error {
|
||||
user, err := u.getUserById(ctx, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !u.deps.Password.Compare(oldPassword, user.Secret) {
|
||||
return ErrUserWrongPassword
|
||||
}
|
||||
|
||||
return u.updatePassword(ctx, *user, newPassword)
|
||||
}
|
||||
|
||||
func (u *userService) updatePassword(ctx context.Context, user models.UserDTO, newPassword string) error {
|
||||
if err := u.deps.Password.Validate(newPassword); err != nil {
|
||||
return ErrUserBadPassword
|
||||
}
|
||||
|
||||
u.deps.UserCache.Del(user.Id)
|
||||
|
||||
newSecret, err := u.deps.Password.Hash(newPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return u.deps.UserRepo.UpdateUser(ctx, user.Id, models.UserUpdateDTO{
|
||||
Secret: newSecret,
|
||||
Name: user.Name,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *userService) getUserById(ctx context.Context, userId string) (*models.UserDTO, error) {
|
||||
if user, ok := u.deps.UserCache.Get(userId); ok {
|
||||
return &user, nil
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user