From f4684be37d13a93b103d7ed3652d89cbd7388bf1 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Sun, 4 Apr 2021 16:36:13 +0600 Subject: [PATCH] Add hashing for link password and update link update service method --- go.mod | 3 +- link/dto.go | 2 -- link/handlers.go | 33 +++++++--------------- link/service.go | 73 ++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 79 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index ab59fcd..96d31a5 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,9 @@ require ( github.com/knadh/koanf v0.15.0 github.com/labstack/echo/v4 v4.2.1 github.com/mitchellh/copystructure v1.1.1 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/rs/zerolog v1.20.0 + golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect golang.org/x/text v0.3.5 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/link/dto.go b/link/dto.go index 511f174..c811153 100644 --- a/link/dto.go +++ b/link/dto.go @@ -16,7 +16,6 @@ type ResourceModel struct { Id string `json:"id"` Name string `json:"name"` RedirectURL string `json:"redirectUrl"` - Password string `json:"password"` CreationTime int64 `json:"creationTime"` } @@ -46,7 +45,6 @@ func MapEntityToModel(entity *Link) ResourceModel { Id: entity.Id, Name: entity.Name, RedirectURL: entity.RedirectURL.String(), - Password: entity.Password, CreationTime: entity.CreationTime.Unix(), } } diff --git a/link/handlers.go b/link/handlers.go index be10ada..10a25c6 100644 --- a/link/handlers.go +++ b/link/handlers.go @@ -92,33 +92,20 @@ func updateHandler(ctx echo.Context, serv Service) error { return echo.NewHTTPError(http.StatusBadRequest, "Invalid data format.") } - updatingLink, err := serv.GetById(linkId) + var parsedUrl *url.URL + parsedUrl, err := url.Parse(model.RedirectURL) if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid URL value.") + } + + if err := serv.UpdateById(linkId, struct { + Name string + Password string + RedirectURL *url.URL + }{Name: model.Name, Password: model.Password, RedirectURL: parsedUrl}); err != nil { return err } - hasChanges := false - switch { - case model.Name != "" && model.Name != updatingLink.Name: - updatingLink.Name = model.Name - - hasChanges = true - case model.RedirectURL != "" && model.RedirectURL != updatingLink.RedirectURL.String(): - if parsedUrl, err := url.Parse(model.RedirectURL); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid URL value.") - } else { - updatingLink.RedirectURL = *parsedUrl - } - - hasChanges = true - } - - if hasChanges { - if err = serv.Update(updatingLink); err != nil { - return err - } - } - return ctx.NoContent(http.StatusOK) } diff --git a/link/service.go b/link/service.go index 5c4b092..26f048f 100644 --- a/link/service.go +++ b/link/service.go @@ -3,6 +3,7 @@ package link import ( apperrors "cgnolink/errors" "github.com/patrickmn/go-cache" + "golang.org/x/crypto/bcrypt" "net/url" ) @@ -13,7 +14,14 @@ type Service interface { GetById(id string) (*Link, error) AccessLink(id string, password string) (*url.URL, error) GetAll(limit int, offset int) (Links, error) - Update(data *Link) error + UpdateById( + id string, + data struct { + Name string + Password string + RedirectURL *url.URL + }, + ) error DeleteById(id string) error } @@ -29,7 +37,7 @@ func (service *PgService) AccessLink(id string, password string) (*url.URL, erro } if link.Password != "" { - if password == "" || link.Password != password { + if password == "" || bcrypt.CompareHashAndPassword([]byte(link.Password), []byte(password)) != nil { return nil, linkNotFoundError } } @@ -54,6 +62,15 @@ func (service *PgService) Create(link *Link) error { return apperrors.AlreadyExistsError{Message: "Link with given ID already exists."} } + if link.Password != "" { + hashedPassword, err := HashPassword(link.Password) + if err != nil { + return err + } + + link.Password = hashedPassword + } + if err = service.rep.Save(link); err != nil { return apperrors.UnknownError{Err: err} } @@ -91,11 +108,46 @@ func (service *PgService) GetAll(limit int, offset int) (Links, error) { return links, nil } -func (service *PgService) Update(data *Link) error { - if err := service.rep.Update(data); err != nil { - return apperrors.UnknownError{Err: err} +func (service *PgService) UpdateById( + id string, + data struct { + Name string + Password string + RedirectURL *url.URL + }, +) error { + link, err := service.GetById(id) + if err != nil { + return err + } + + hasChanges := false + switch { + case data.Name != "": + link.Name = data.Name + + hasChanges = true + case data.RedirectURL != nil: + link.RedirectURL = *data.RedirectURL + + hasChanges = true + case data.Password != "": + hashedPw, err := HashPassword(data.Password) + if err != nil { + return err + } + + link.Password = hashedPw + + hasChanges = true + } + + if hasChanges { + if err := service.rep.Update(link); err != nil { + return apperrors.UnknownError{Err: err} + } + service.cache.Delete(link.Id) } - service.cache.Delete(data.Id) return nil } @@ -108,3 +160,12 @@ func (service *PgService) DeleteById(id string) error { return nil } + +func HashPassword(password string) (string, error) { + hashedPw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "", apperrors.UnknownError{Err: err} + } + + return string(hashedPw), nil +}