From 36296f23dd880806d277fbef2a7e5a95d7fe1997 Mon Sep 17 00:00:00 2001 From: Erhan Yakut Date: Mon, 8 Jun 2020 01:03:54 +0300 Subject: [PATCH] changed SHA256 to bcrypt --- internal/api/auth.go | 71 +++++++++++++++++++-- internal/api/login.go | 14 ++--- internal/api/signup.go | 80 ------------------------ internal/app/encryption.go | 11 ++-- internal/app/encryption_test.go | 24 ------- internal/app/login.go | 4 +- internal/app/user.go | 6 +- internal/router/router.go | 16 ++--- internal/storage/user/user_repository.go | 15 ++++- 9 files changed, 101 insertions(+), 140 deletions(-) delete mode 100644 internal/api/signup.go delete mode 100644 internal/app/encryption_test.go diff --git a/internal/api/auth.go b/internal/api/auth.go index 057d8cf..92cb9f7 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -18,8 +18,74 @@ var ( InvalidToken = "Token is expired or not valid!" NoToken = "Token could not found! " TokenCreateErr = "Token could not be created" + SignupSuccess = "User created successfully" ) +// Signup ... +func Signup(s storage.Store) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + userDTO := new(model.UserDTO) + + // 1. Decode request body to userDTO object + decoder := json.NewDecoder(r.Body) + if err := decoder.Decode(&userDTO); err != nil { + RespondWithError(w, http.StatusBadRequest, "Invalid resquest payload") + return + } + defer r.Body.Close() + + // 2. Run validator according to model.UserDTO validator tags + validate := validator.New() + validateError := validate.Struct(userDTO) + if validateError != nil { + errs := GetErrors(validateError.(validator.ValidationErrors)) + RespondWithErrors(w, http.StatusBadRequest, InvalidRequestPayload, errs) + return + } + + // 3. Check if user exist in database + _, err := s.Users().FindByEmail(userDTO.Email) + if err == nil { + errs := []string{"This email is already used!"} + message := "User couldn't created!" + RespondWithErrors(w, http.StatusBadRequest, message, errs) + return + } + + // 4. Create new user + createdUser, err := app.CreateUser(s, userDTO) + if err != nil { + RespondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + // 5. Update user once to generate schema + updatedUser, err := app.GenerateSchema(s, createdUser) + if err != nil { + RespondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + // 6. Create user schema and tables + err = s.Users().CreateSchema(updatedUser.Schema) + if err != nil { + RespondWithError(w, http.StatusInternalServerError, err.Error()) + return + } + + // 7. Create user tables in user schema + app.MigrateUserTables(s, updatedUser.Schema) + + response := model.Response{ + Code: http.StatusOK, + Status: Success, + Message: SignupSuccess, + } + RespondWithJSON(w, http.StatusOK, response) + } +} + // Signin ... func Signin(s storage.Store) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -43,13 +109,10 @@ func Signin(s storage.Store) http.HandlerFunc { return } - // Create hash from master password - loginDTO.MasterPassword = app.NewSHA256([]byte(loginDTO.MasterPassword)) - // Check if user exist in database and credentials are true user, err := s.Users().FindByCredentials(loginDTO.Email, loginDTO.MasterPassword) if err != nil { - RespondWithError(w, http.StatusUnauthorized, InvalidUser) + RespondWithError(w, http.StatusUnauthorized, err.Error()) return } diff --git a/internal/api/login.go b/internal/api/login.go index 7f3bf29..5f43eb2 100644 --- a/internal/api/login.go +++ b/internal/api/login.go @@ -33,7 +33,7 @@ func FindAllLogins(s storage.Store) http.HandlerFunc { return } - loginList = app.DecryptLoginPasswords(loginList) + // loginList = app.DecryptLoginPasswords(loginList) RespondWithJSON(w, http.StatusOK, loginList) } } @@ -55,13 +55,13 @@ func FindLoginsByID(s storage.Store) http.HandlerFunc { return } - uLogin, err := app.DecryptLoginPassword(s, login) - if err != nil { - RespondWithError(w, http.StatusInternalServerError, err.Error()) - return - } + // uLogin, err := app.DecryptLoginPassword(s, login) + // if err != nil { + // RespondWithError(w, http.StatusInternalServerError, err.Error()) + // return + // } - RespondWithJSON(w, http.StatusOK, model.ToLoginDTO(uLogin)) + RespondWithJSON(w, http.StatusOK, model.ToLoginDTO(login)) } } diff --git a/internal/api/signup.go b/internal/api/signup.go deleted file mode 100644 index 14f9981..0000000 --- a/internal/api/signup.go +++ /dev/null @@ -1,80 +0,0 @@ -package api - -import ( - "encoding/json" - "net/http" - - "github.com/go-playground/validator/v10" - "github.com/pass-wall/passwall-server/internal/app" - "github.com/pass-wall/passwall-server/internal/storage" - "github.com/pass-wall/passwall-server/model" -) - -const ( - SignupSuccess = "User created successfully" -) - -// Signup ... -func Signup(s storage.Store) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - - userDTO := new(model.UserDTO) - - // 1. Decode request body to userDTO object - decoder := json.NewDecoder(r.Body) - if err := decoder.Decode(&userDTO); err != nil { - RespondWithError(w, http.StatusBadRequest, "Invalid resquest payload") - return - } - defer r.Body.Close() - - // 2. Run validator according to model.UserDTO validator tags - validate := validator.New() - validateError := validate.Struct(userDTO) - if validateError != nil { - errs := GetErrors(validateError.(validator.ValidationErrors)) - RespondWithErrors(w, http.StatusBadRequest, InvalidRequestPayload, errs) - return - } - - // 3. Check if user exist in database - _, err := s.Users().FindByEmail(userDTO.Email) - if err == nil { - errs := []string{"This email is already used!"} - message := "User couldn't created!" - RespondWithErrors(w, http.StatusBadRequest, message, errs) - return - } - - // 4. Create new user - createdUser, err := app.CreateUser(s, userDTO) - if err != nil { - RespondWithError(w, http.StatusInternalServerError, err.Error()) - return - } - - // 5. Update user once to generate schema - updatedUser, err := app.GenerateSchema(s, createdUser) - if err != nil { - RespondWithError(w, http.StatusInternalServerError, err.Error()) - return - } - - // 6. Create user schema and tables - err = s.Users().CreateSchema(updatedUser.Schema) - if err != nil { - RespondWithError(w, http.StatusInternalServerError, err.Error()) - return - } - - // 7. Create user tables in user schema - app.MigrateUserTables(s, updatedUser.Schema) - - response := model.Response{ - Code: http.StatusOK, - Status: Success, - Message: SignupSuccess, - } - RespondWithJSON(w, http.StatusOK, response) - } -} diff --git a/internal/app/encryption.go b/internal/app/encryption.go index 17c891d..b80a55d 100644 --- a/internal/app/encryption.go +++ b/internal/app/encryption.go @@ -5,13 +5,13 @@ import ( "crypto/cipher" "crypto/md5" "crypto/rand" - "crypto/sha256" "encoding/hex" "io" "io/ioutil" "os" "github.com/sethvargo/go-password/password" + "golang.org/x/crypto/bcrypt" "github.com/spf13/viper" ) @@ -36,11 +36,10 @@ func Password() (string, error) { return res, nil } -// NewSHA256 ... -func NewSHA256(key []byte) string { - hasher := sha256.New() - hasher.Write([]byte(key)) - return hex.EncodeToString(hasher.Sum(nil)) +// NewBcrypt ... +func NewBcrypt(key []byte) string { + hasher, _ := bcrypt.GenerateFromPassword(key, bcrypt.DefaultCost) + return string(hasher) } // CreateHash ... diff --git a/internal/app/encryption_test.go b/internal/app/encryption_test.go deleted file mode 100644 index 6153ccb..0000000 --- a/internal/app/encryption_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package app - -import ( - "fmt" - "testing" -) - -func TestNewSHA256(t *testing.T) { - for i, tt := range []struct { - in []byte - out string - }{ - {[]byte(""), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, - {[]byte("abc"), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}, - {[]byte("hello"), "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}, - } { - t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { - result := NewSHA256(tt.in) - if result != tt.out { - t.Errorf("want %v; got %v", tt.out, result) - } - }) - } -} \ No newline at end of file diff --git a/internal/app/login.go b/internal/app/login.go index 9dcaed9..75d1dfd 100644 --- a/internal/app/login.go +++ b/internal/app/login.go @@ -12,7 +12,7 @@ import ( func CreateLogin(s storage.Store, dto *model.LoginDTO, schema string) (*model.Login, error) { rawPass := dto.Password - dto.Password = base64.StdEncoding.EncodeToString(Encrypt(dto.Password, viper.GetString("server.passphrase"))) + // dto.Password = base64.StdEncoding.EncodeToString(Encrypt(dto.Password, viper.GetString("server.passphrase"))) createdLogin, err := s.Logins().Save(model.ToLogin(dto), schema) if err != nil { @@ -35,7 +35,7 @@ func UpdateLogin(s storage.Store, login *model.Login, dto *model.LoginDTO, schem dto.Password = generatedPass } rawPass := dto.Password - dto.Password = base64.StdEncoding.EncodeToString(Encrypt(dto.Password, viper.GetString("server.passphrase"))) + // dto.Password = base64.StdEncoding.EncodeToString(Encrypt(dto.Password, viper.GetString("server.passphrase"))) login.URL = dto.URL login.Username = dto.Username diff --git a/internal/app/user.go b/internal/app/user.go index 3aac394..842ac76 100644 --- a/internal/app/user.go +++ b/internal/app/user.go @@ -12,7 +12,7 @@ import ( func CreateUser(s storage.Store, userDTO *model.UserDTO) (*model.User, error) { // Hasing the master password with SHA256 - userDTO.MasterPassword = NewSHA256([]byte(userDTO.MasterPassword)) + userDTO.MasterPassword = NewBcrypt([]byte(userDTO.MasterPassword)) // New user's plan is Free and role is Member (not Admin) userDTO.Plan = "Free" @@ -33,8 +33,8 @@ func CreateUser(s storage.Store, userDTO *model.UserDTO) (*model.User, error) { func UpdateUser(s storage.Store, user *model.User, userDTO *model.UserDTO, isAuthorized bool) (*model.User, error) { // TODO: Refactor the contents of updated user with a logical way - if userDTO.MasterPassword != "" && NewSHA256([]byte(userDTO.MasterPassword)) != user.MasterPassword { - userDTO.MasterPassword = NewSHA256([]byte(userDTO.MasterPassword)) + if userDTO.MasterPassword != "" && NewBcrypt([]byte(userDTO.MasterPassword)) != user.MasterPassword { + userDTO.MasterPassword = NewBcrypt([]byte(userDTO.MasterPassword)) } else { userDTO.MasterPassword = user.MasterPassword } diff --git a/internal/router/router.go b/internal/router/router.go index df5a31e..d5dbc75 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -92,13 +92,10 @@ func (r *Router) initRoutes() { // Auth endpoints authRouter := mux.NewRouter().PathPrefix("/auth").Subrouter() - authRouter.HandleFunc("/signin", api.Signin(r.store)) - authRouter.HandleFunc("/refresh", api.RefreshToken(r.store)) - authRouter.HandleFunc("/check", api.CheckToken(r.store)) - - // Web public endpoints - webRouter := mux.NewRouter().PathPrefix("/web").Subrouter() - webRouter.HandleFunc("/signup", api.Signup(r.store)).Methods(http.MethodPost) + authRouter.HandleFunc("/signup", api.Signup(r.store)).Methods(http.MethodPost) + authRouter.HandleFunc("/signin", api.Signin(r.store)).Methods(http.MethodPost) + authRouter.HandleFunc("/refresh", api.RefreshToken(r.store)).Methods(http.MethodPost) + authRouter.HandleFunc("/check", api.CheckToken(r.store)).Methods(http.MethodPost) n := negroni.Classic() n.Use(negroni.HandlerFunc(CORS)) @@ -114,11 +111,6 @@ func (r *Router) initRoutes() { negroni.Wrap(authRouter), )) - r.router.PathPrefix("/web").Handler(n.With( - LimitHandler(), - negroni.Wrap(webRouter), - )) - // Insecure endpoints r.router.HandleFunc("/health", api.HealthCheck(r.store)).Methods(http.MethodGet) diff --git a/internal/storage/user/user_repository.go b/internal/storage/user/user_repository.go index 7216094..d47db79 100644 --- a/internal/storage/user/user_repository.go +++ b/internal/storage/user/user_repository.go @@ -5,6 +5,7 @@ import ( "github.com/jinzhu/gorm" "github.com/pass-wall/passwall-server/model" + "golang.org/x/crypto/bcrypt" ) // Repository ... @@ -66,8 +67,18 @@ func (p *Repository) FindByEmail(email string) (*model.User, error) { // FindByCredentials ... func (p *Repository) FindByCredentials(email, masterPassword string) (*model.User, error) { user := new(model.User) - err := p.db.Where(`email = ? AND master_password = ?`, email, masterPassword).First(&user).Error - return user, err + err := p.db.Where(`email = ?`, email).First(&user).Error + if err != nil { + return user, err + } + + // Comparing the password with the bcrypt hash + err = bcrypt.CompareHashAndPassword([]byte(user.MasterPassword), []byte(masterPassword)) + if err != nil { + return user, err + } + + return user, nil } // Save ...