From 7670aa4bd3cce5caf4683a0acd6a2cd5185f6822 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Tue, 8 Sep 2020 03:07:07 +0530 Subject: [PATCH 01/51] Updated Migration For User Table --- migrations/1587381324_create_users.up.sql | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/migrations/1587381324_create_users.up.sql b/migrations/1587381324_create_users.up.sql index f893282..91b8178 100644 --- a/migrations/1587381324_create_users.up.sql +++ b/migrations/1587381324_create_users.up.sql @@ -1,4 +1,10 @@ -CREATE TABLE users ( - name text, - age integer -); +CREATE TABLE IF NOT EXISTS users ( + id BIGSERIAL NOT NULL PRIMARY KEY, + first_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + mobile VARCHAR(20) NOT NULL UNIQUE, + address TEXT, + password VARCHAR(50), + created_at TIMESTAMP DEFAULT (NOW() AT TIME ZONE 'UTC') +); \ No newline at end of file From 986fe2929ad87df79a69269cef48188e416b8d58 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Tue, 8 Sep 2020 03:28:27 +0530 Subject: [PATCH 02/51] Added GetUserByMobile method in Storer Interface --- db/user.go | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/db/user.go b/db/user.go index 17715e9..41bdbb2 100644 --- a/db/user.go +++ b/db/user.go @@ -2,17 +2,25 @@ package db import ( "context" - + "database/sql" logger "github.com/sirupsen/logrus" ) +const ( + getUserByMobileQuery = `SELECT * FROM users WHERE email=$1 LIMIT 1` +) + type User struct { - Name string `db:"name" json:"full_name"` - Age int `db:"age" json:"age"` + FirstName string `db:"first_name" json:"first_name"` + LastName string `db:"last_name" json:"last_name"` + Email string `db:"email" json:"email"` + Mobile string `db:"mobile" json:"mobile"` + Address string `db:"address" json:"address"` + Password string `db:"password" json:"password"` } func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { - err = s.db.Select(&users, "SELECT * FROM users ORDER BY name ASC") + err = s.db.Select(&users, "SELECT * FROM users ORDER BY first_name ASC") if err != nil { logger.WithField("err", err.Error()).Error("Error listing users") return @@ -20,3 +28,17 @@ func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { return } + +// GetUserByMobile - Given a mobile number, return that user. +func (s *pgStore) GetUserByMobile(ctx context.Context, mobile string) (user User, err error) { + err = s.db.Get(&user, getUserByMobileQuery, mobile) + if err != nil { + if err == sql.ErrNoRows { + logger.WithField("err", err.Error()).Error("User with mobile number " + mobile + "doenot Exist") + } + // Possible that there's no rows in the result set + logger.WithField("err", err.Error()).Error("Error selecting user from database by mobile number " + mobile) + return + } + return +} From 39734b45c7822cc077acf0044eb9808544d88944 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Tue, 8 Sep 2020 03:29:48 +0530 Subject: [PATCH 03/51] Called GetUserByMobile in Storer Interface --- db/db.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/db/db.go b/db/db.go index 2d1556a..ced37a1 100644 --- a/db/db.go +++ b/db/db.go @@ -4,8 +4,10 @@ import ( "context" ) +// Storer - an interface we use to expose methods that do stuff to the underlying database type Storer interface { ListUsers(context.Context) ([]User, error) + GetUserByMobile(context.Context, string) (User, error) //Create(context.Context, User) error //GetUser(context.Context) (User, error) //Delete(context.Context, string) error From adcd53da0aa6a9d643d08f69a4ec3da4c537f863 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Tue, 8 Sep 2020 15:21:16 +0530 Subject: [PATCH 04/51] Updated Users Table --- migrations/1587381324_create_users.up.sql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/migrations/1587381324_create_users.up.sql b/migrations/1587381324_create_users.up.sql index 91b8178..7b7846a 100644 --- a/migrations/1587381324_create_users.up.sql +++ b/migrations/1587381324_create_users.up.sql @@ -1,9 +1,12 @@ CREATE TABLE IF NOT EXISTS users ( - id BIGSERIAL NOT NULL PRIMARY KEY, + id SERIAL NOT NULL PRIMARY KEY, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, mobile VARCHAR(20) NOT NULL UNIQUE, + country VARCHAR(100), + state VARCHAR(100), + city VARCHAR(100), address TEXT, password VARCHAR(50), created_at TIMESTAMP DEFAULT (NOW() AT TIME ZONE 'UTC') From 3560764be7d8d1e5efb1ccbeae89fa68427addd0 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:48:22 +0530 Subject: [PATCH 05/51] Updated User Table --- migrations/1587381324_create_users.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/1587381324_create_users.up.sql b/migrations/1587381324_create_users.up.sql index 7b7846a..65f44a2 100644 --- a/migrations/1587381324_create_users.up.sql +++ b/migrations/1587381324_create_users.up.sql @@ -8,6 +8,6 @@ CREATE TABLE IF NOT EXISTS users ( state VARCHAR(100), city VARCHAR(100), address TEXT, - password VARCHAR(50), + password TEXT, created_at TIMESTAMP DEFAULT (NOW() AT TIME ZONE 'UTC') ); \ No newline at end of file From 51f2149d889e0fd2e960f1e8541f0c3de4bb8091 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:50:31 +0530 Subject: [PATCH 06/51] Added Routes and JWT Middleware --- service/router.go | 72 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/service/router.go b/service/router.go index 121ad64..d9593c4 100644 --- a/service/router.go +++ b/service/router.go @@ -2,18 +2,19 @@ package service import ( "fmt" - "net/http" - - "joshsoftware/go-e-commerce/config" - + "github.com/dgrijalva/jwt-go" "github.com/gorilla/mux" + logger "github.com/sirupsen/logrus" + "joshsoftware/go-e-commerce/config" + "net/http" + "strconv" ) const ( versionHeader = "Accept" ) -/* The routing mechanism. Mux helps us define handler functions and the access methods */ +/*InitRouter is The routing mechanism. Mux helps us define handler functions and the access methods */ func InitRouter(deps Dependencies) (router *mux.Router) { router = mux.NewRouter() @@ -23,6 +24,67 @@ func InitRouter(deps Dependencies) (router *mux.Router) { // Version 1 API management v1 := fmt.Sprintf("application/vnd.%s.v1", config.AppName()) + //Route for User Login + router.HandleFunc("/login", userLoginHandler(deps)).Methods(http.MethodPost).Headers(versionHeader, v1) + + //Router for Get User from ID + router.Handle("/user/{id}", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) + + //Router for Get User from ID + router.Handle("/user/{id}/logout", jwtMiddleWare(userLogoutHandler(deps), deps)).Methods(http.MethodDelete).Headers(versionHeader, v1) + + //Router for Get All Users router.HandleFunc("/users", listUsersHandler(deps)).Methods(http.MethodGet).Headers(versionHeader, v1) return } + +func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + + mySigningKey := config.JWTKey() + + var idParam = mux.Vars(req)["id"] + validID, ok := strconv.Atoi(idParam) + + if ok != nil { + logger.Error(ok.Error()) + } + authToken := req.Header["Token"] + if authToken != nil { + token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("There was an error") + } + return mySigningKey, nil + }) + if err != nil { + if err == jwt.ErrSignatureInvalid { + rw.WriteHeader(http.StatusUnauthorized) + return + } + rw.WriteHeader(http.StatusBadRequest) + return + } + claims, ok := token.Claims.(jwt.MapClaims) + + disdis := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]) + + fmt.Println("DIsDis : ", disdis) + if !ok && !token.Valid && !disdis { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } + + userID := claims["id"] + + if float64(validID) != userID { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } + + endpoint.ServeHTTP(rw, req) + } + }) +} From 1f192ceab9e3fdb1f480a604f94726a0fd415a82 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:51:37 +0530 Subject: [PATCH 07/51] Added JWT Token Generate and Logout Methods --- service/session_http.go | 127 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 service/session_http.go diff --git a/service/session_http.go b/service/session_http.go new file mode 100644 index 0000000..6161f14 --- /dev/null +++ b/service/session_http.go @@ -0,0 +1,127 @@ +package service + +import ( + "encoding/json" + "fmt" + "github.com/dgrijalva/jwt-go" + logger "github.com/sirupsen/logrus" + ae "joshsoftware/go-e-commerce/apperrors" + "joshsoftware/go-e-commerce/config" + "joshsoftware/go-e-commerce/db" + "net/http" + "time" +) + +func generateJwt(userID int) (tokenString string, err error) { + mySigningKey := config.JWTKey() + if mySigningKey == nil { + ae.Error(ae.ErrNoSigningKey, "Application error: No signing key configured", err) + return + } + + token := jwt.New(jwt.SigningMethodHS256) + claims := token.Claims.(jwt.MapClaims) + claims["id"] = userID + claims["exp"] = time.Now().Add(time.Duration(config.JWTExpiryDurationHours()) * time.Hour).Unix() + + tokenString, err = token.SignedString(mySigningKey) + if err != nil { + ae.Error(ae.ErrSignedString, "Failed To Get Signed String", err) + return + } + return +} + +// @Title userLogin +// @Description Logging User in +// @Router /login [post] +// @Accept json +// @Success 200 {object} +// @Failure 400 {object} +func userLoginHandler(deps Dependencies) http.HandlerFunc { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + user := db.User{} + err := json.NewDecoder(req.Body).Decode(&user) + if err != nil { + logger.WithField("err", err.Error()).Error("JSON Decoding Failed") + rw.WriteHeader(http.StatusBadRequest) + return + } + + user, err1 := deps.Store.AuthenticateUser(req.Context(), user) + if err1 != nil { + logger.WithField("err", err1.Error()).Error("Invalid Credentials") + rw.WriteHeader(http.StatusInternalServerError) + return + } + + token, err := generateJwt(user.ID) + if err != nil { + ae.Error(ae.ErrUnknown, "Unknown/unexpected error while creating JWT", err) + ae.JSONError(rw, http.StatusInternalServerError, err) + return + } + + respBytes, err := json.Marshal(user) + + rw.Header().Add("Authorization", token) + rw.Header().Add("Content-Type", "application/json") + rw.Write(respBytes) + }) +} + +func userLogoutHandler(deps Dependencies) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + + mySigningKey := config.JWTKey() + + authToken := req.Header["Token"] + if authToken != nil { + token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("There was an error") + } + return mySigningKey, nil + }) + if err != nil { + if err == jwt.ErrSignatureInvalid { + rw.WriteHeader(http.StatusUnauthorized) + return + } + rw.WriteHeader(http.StatusBadRequest) + return + } + claims, ok := token.Claims.(jwt.MapClaims) + + if !ok && !token.Valid { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } + + userID := claims["id"].(float64) + + if err != nil { + logger.WithField("err", err.Error()).Error("Conversion Failed") + } + expirationTimeStamp := int64(claims["exp"].(float64)) + expirationDate := time.Unix(expirationTimeStamp, 0) + + userBlackListedToken := db.BlacklistedToken{ + UserID: userID, + ExpirationDate: expirationDate, + Token: authToken[0], + } + + err = deps.Store.CreateBlacklistedToken(req.Context(), userBlackListedToken) + if err != nil { + ae.Error(ae.ErrFailedToCreate, "Error creating blaclisted token record", err) + rw.Header().Add("Content-Type", "application/json") + ae.JSONError(rw, http.StatusInternalServerError, err) + return + } + rw.Header().Add("Content-Type", "application/json") + return + } + }) +} From 568b0c0a02e26f6279b02397c76b2cffb5fba90f Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:52:36 +0530 Subject: [PATCH 08/51] Added Necessary Comments --- service/dependencies.go | 1 + 1 file changed, 1 insertion(+) diff --git a/service/dependencies.go b/service/dependencies.go index 431979b..071be44 100644 --- a/service/dependencies.go +++ b/service/dependencies.go @@ -2,6 +2,7 @@ package service import "joshsoftware/go-e-commerce/db" +//Dependencies Structure type Dependencies struct { Store db.Storer // define other service dependencies From 15c11c66b8b146a830accca6db3d56b8d4be5dda Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:53:24 +0530 Subject: [PATCH 09/51] Added Necessary Comments --- service/ping_http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/service/ping_http.go b/service/ping_http.go index d30d164..db0743f 100644 --- a/service/ping_http.go +++ b/service/ping_http.go @@ -7,6 +7,7 @@ import ( logger "github.com/sirupsen/logrus" ) +//PingResponse Struct type PingResponse struct { Message string `json:"message"` } From 967cd6288b1df69cd55d75c835ba6925c7d6e938 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:54:31 +0530 Subject: [PATCH 10/51] Imported New Libraries --- go.mod | 2 ++ go.sum | 2 ++ 2 files changed, 4 insertions(+) diff --git a/go.mod b/go.mod index bd1ae4f..f725b07 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module joshsoftware/go-e-commerce go 1.14 require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gorilla/mux v1.8.0 github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.8.0 @@ -12,4 +13,5 @@ require ( github.com/stretchr/testify v1.6.1 github.com/urfave/cli v1.22.4 github.com/urfave/negroni v1.0.0 + golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 ) diff --git a/go.sum b/go.sum index 0b155a0..73606e8 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -203,6 +204,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= From dbb75ae81e81f8a7d8aa1cc7696b750ce5ee5e53 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:55:44 +0530 Subject: [PATCH 11/51] Added Methods to Fetch JWT SecretKey and ExpirationTime --- config/config.go | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/config/config.go b/config/config.go index af428d1..16ef6c1 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,6 @@ package config import ( - "errors" "fmt" "strconv" @@ -9,12 +8,15 @@ import ( ) var ( - appName string - appPort int + appName string + appPort int + jwtKey string + jwtExpiryDurationHours int ) +// Load - loads all the environment variables and/or params in application.yml func Load() { - viper.SetDefault("APP_NAME", "app") + viper.SetDefault("APP_NAME", "e-commerce") viper.SetDefault("APP_PORT", "8002") viper.SetConfigName("application") @@ -24,8 +26,13 @@ func Load() { viper.AddConfigPath("./../..") viper.ReadInConfig() viper.AutomaticEnv() + + // Check for the presence of JWT_KEY and JWT_EXPIRY_DURATION_HOURS + JWTKey() + JWTExpiryDurationHours() } +// AppName - returns the app name func AppName() string { if appName == "" { appName = ReadEnvString("APP_NAME") @@ -33,6 +40,7 @@ func AppName() string { return appName } +// AppPort - returns application http port func AppPort() int { if appPort == 0 { appPort = ReadEnvInt("APP_PORT") @@ -40,6 +48,17 @@ func AppPort() int { return appPort } +// JWTKey - returns the JSON Web Token key +func JWTKey() []byte { + return []byte(ReadEnvString("JWT_SECRET")) +} + +// JWTExpiryDurationHours - returns duration for jwt expiry in int +func JWTExpiryDurationHours() int { + return int(ReadEnvInt("JWT_EXPIRY_DURATION_HOURS")) +} + +// ReadEnvInt - reads an environment variable as an integer func ReadEnvInt(key string) int { checkIfSet(key) v, err := strconv.Atoi(viper.GetString(key)) @@ -49,11 +68,13 @@ func ReadEnvInt(key string) int { return v } +// ReadEnvString - reads an environment variable as a string func ReadEnvString(key string) string { checkIfSet(key) return viper.GetString(key) } +// ReadEnvBool - reads environment variable as a boolean func ReadEnvBool(key string) bool { checkIfSet(key) return viper.GetBool(key) @@ -61,7 +82,7 @@ func ReadEnvBool(key string) bool { func checkIfSet(key string) { if !viper.IsSet(key) { - err := errors.New(fmt.Sprintf("Key %s is not set", key)) + err := fmt.Errorf("Key %s is not set", key) panic(err) } } From dcbaa0176eef937d8e78e3b21bf782928f827b55 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:57:28 +0530 Subject: [PATCH 12/51] Added getUserHandler --- service/user_http.go | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/service/user_http.go b/service/user_http.go index c544bcd..4196736 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -2,9 +2,10 @@ package service import ( "encoding/json" - "net/http" - + "github.com/gorilla/mux" logger "github.com/sirupsen/logrus" + "net/http" + "strconv" ) // @Title listUsers @@ -33,3 +34,35 @@ func listUsersHandler(deps Dependencies) http.HandlerFunc { rw.Write(respBytes) }) } + +// @Title getUser +// @Description Show User User +// @Router /user/{id} [get] +// @Accept json +// @Success 200 {object} +// @Failure 400 {object} +func getUserHandler(deps Dependencies) http.HandlerFunc { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + var idParam = mux.Vars(req)["id"] + id, err := strconv.Atoi(idParam) + if err != nil { + logger.Error("COnversion Failed") + } + user, err := deps.Store.GetUser(req.Context(), id) + if err != nil { + logger.WithField("err", err.Error()).Error("Error fetching data") + rw.WriteHeader(http.StatusInternalServerError) + return + } + + respBytes, err := json.Marshal(user) + if err != nil { + logger.WithField("err", err.Error()).Error("Error marshaling users data") + rw.WriteHeader(http.StatusInternalServerError) + return + } + + rw.Header().Add("Content-Type", "application/json") + rw.Write(respBytes) + }) +} From b7e61b81f5bd9197a2f74b4cf70cb12a3657a5fa Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:58:09 +0530 Subject: [PATCH 13/51] Added Necessary Comments --- db/pg.go | 1 + 1 file changed, 1 insertion(+) diff --git a/db/pg.go b/db/pg.go index bd23fc1..eb56cf5 100644 --- a/db/pg.go +++ b/db/pg.go @@ -10,6 +10,7 @@ import ( "time" "github.com/jmoiron/sqlx" + //lib/pq internally configures with database/sql library" _ "github.com/lib/pq" "github.com/mattes/migrate" "github.com/mattes/migrate/database/postgres" From 31a01aa6b630d7ce96293975b8dca70ad033bf64 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 10:59:29 +0530 Subject: [PATCH 14/51] Added Necessary methods for user sql operations --- db/user.go | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/db/user.go b/db/user.go index 41bdbb2..04595e9 100644 --- a/db/user.go +++ b/db/user.go @@ -4,19 +4,23 @@ import ( "context" "database/sql" logger "github.com/sirupsen/logrus" + "golang.org/x/crypto/bcrypt" + ae "joshsoftware/go-e-commerce/apperrors" ) -const ( - getUserByMobileQuery = `SELECT * FROM users WHERE email=$1 LIMIT 1` -) - +//User Struct for declaring attributes of User type User struct { + ID int `db:"id" json:"id"` FirstName string `db:"first_name" json:"first_name"` LastName string `db:"last_name" json:"last_name"` Email string `db:"email" json:"email"` Mobile string `db:"mobile" json:"mobile"` Address string `db:"address" json:"address"` Password string `db:"password" json:"password"` + Country string `db:"country" json:"country"` + State string `db:"state" json:"state"` + City string `db:"city" json:"city"` + CreatedAt string `db:"created_at" json:"created_at"` } func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { @@ -29,16 +33,33 @@ func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { return } -// GetUserByMobile - Given a mobile number, return that user. -func (s *pgStore) GetUserByMobile(ctx context.Context, mobile string) (user User, err error) { - err = s.db.Get(&user, getUserByMobileQuery, mobile) +func (s *pgStore) GetUser(ctx context.Context, id int) (user User, err error) { + + err = s.db.Get(&user, "SELECT * FROM users WHERE id=$1", id) if err != nil { if err == sql.ErrNoRows { - logger.WithField("err", err.Error()).Error("User with mobile number " + mobile + "doenot Exist") + err = ae.ErrRecordNotFound } - // Possible that there's no rows in the result set - logger.WithField("err", err.Error()).Error("Error selecting user from database by mobile number " + mobile) + logger.WithField("err", err.Error()).Error("Query Failed") + return + } + + return +} + +// GetUserByMobile - Given a mobile number, return that user. +func (s *pgStore) AuthenticateUser(ctx context.Context, u User) (user User, err error) { + + err = s.db.Get(&user, "SELECT * FROM users where email = $1", u.Email) + if err != nil { + logger.WithField("err", err.Error()).Error("No such User Available") return } + + if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(u.Password)); err != nil { + // If the two passwords don't match, return a 401 status + logger.WithField("Error", err.Error()) + } + return } From 52edc97470c70514b3efeb96f079ecf4e30c0e84 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 11:00:29 +0530 Subject: [PATCH 15/51] Defined Necessary SQL methods in Interface --- db/db.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/db/db.go b/db/db.go index ced37a1..1bd98e4 100644 --- a/db/db.go +++ b/db/db.go @@ -7,7 +7,10 @@ import ( // Storer - an interface we use to expose methods that do stuff to the underlying database type Storer interface { ListUsers(context.Context) ([]User, error) - GetUserByMobile(context.Context, string) (User, error) + AuthenticateUser(context.Context, User) (User, error) + GetUser(context.Context, int) (User, error) + CreateBlacklistedToken(context.Context, BlacklistedToken) error + CheckBlacklistedToken(context.Context, string) bool //Create(context.Context, User) error //GetUser(context.Context) (User, error) //Delete(context.Context, string) error From f142949765345846b0dafc28d6c71483ab215111 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 11:02:16 +0530 Subject: [PATCH 16/51] File for Handling Error Messages --- apperrors/apperrors.go | 105 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 apperrors/apperrors.go diff --git a/apperrors/apperrors.go b/apperrors/apperrors.go new file mode 100644 index 0000000..eb927fc --- /dev/null +++ b/apperrors/apperrors.go @@ -0,0 +1,105 @@ +/* +======================================================================== +Error Definitions + This file, part of the "apperrors" package, defines all the error + variables by name that'll be used throughout the entire application. +Rules + Every error variable MUST start with "Err" - with a capital E so + we can export it for use in other packages. + Additionally, please place a comment (one or more lines) on the line + before the declaration of the error variable that explains what that + variable is intended to be used for, or the error concept it's meant + to represent. +Notes + There are also additional helper functions in this file pertaining to + miscellaneous error handling. +======================================================================== +*/ + +package apperrors + +import ( + "encoding/json" + "errors" + "fmt" + l "github.com/sirupsen/logrus" + "net/http" +) + +// ErrorStruct - a generic struct you can use to create error messages/logs to be converted +// to JSON or other types of messages/data as you need it +type ErrorStruct struct { + Message string `json:"message,omitempty"` // Your message to the end user or developer + Status int `json:"status,omitempty"` // HTTP status code that should go with the message/log (if any) +} + +// Error - prints out an error +func Error(appError error, msg string, triggeringError error) { + l.WithFields(l.Fields{"appError": appError, "message": msg}).Error(triggeringError) +} + +// Warn - for warnings +func Warn(appError error, msg string, triggeringError error) { + l.WithFields(l.Fields{"appError": appError, "message": msg}).Warn(triggeringError) +} + +// JSONError - This function writes out an error response with the status +// header passed in +func JSONError(rw http.ResponseWriter, status int, err error) { + // Create the ErrorStruct object for later use + errObj := ErrorStruct{ + Message: err.Error(), + Status: status, + } + + errJSON, err := json.Marshal(&errObj) + if err != nil { + Warn(err, "Error in AppErrors marshalling JSON", err) + } + rw.WriteHeader(status) + rw.Header().Add("Content-Type", "application/json") + rw.Write(errJSON) + return +} + +// ErrKeyNotSet - Returns error object specific to the key value passed in +func ErrKeyNotSet(key string) (err error) { + return fmt.Errorf("Key not set: %s", key) +} + +// ErrRecordNotFound - for when a database record isn't found +var ErrRecordNotFound = errors.New("Database record not found") + +// ErrInvalidToken - used when a JSON Web Token ("JWT") cannot be validated +// by the JWT library +var ErrInvalidToken = errors.New("Invalid Token") + +// ErrSignedString - failed to sign the token string +var ErrSignedString = errors.New("Failed to sign token string") + +// ErrMissingAuthHeader - When the HTTP request doesn't contain an 'Authorization' header +var ErrMissingAuthHeader = errors.New("Missing Auth header") + +// ErrJSONParseFail - for some reason, the call to json.Unmarshal or json.Marshal returned an error +var ErrJSONParseFail = errors.New("Failed to parse JSON response (likely not valid JSON)") + +// ErrReadingResponseBody - If for some reason the app can't read the HTTP response body +// issued by another server (used when we try to read user information via oauth during +// login process) +var ErrReadingResponseBody = errors.New("Could not read HTTP response body") + +// ErrHTTPRequestFailed - The HTTP request we issued failed for some reason +var ErrHTTPRequestFailed = errors.New("HTTP Request Failed") + +// ErrNoSigningKey - there isn't a signing key defined in the app configuration +var ErrNoSigningKey = errors.New("no JWT signing key specified; cannot authenticate users. Define JWT_SECRET in application.yml and restart") + +// ErrFailedToCreate - Failed to create record in database +var ErrFailedToCreate = errors.New("Failed to create database record") + +// ----- +// Let's make the more "generic" errors dead last in our file +// ----- + +// ErrUnknown - Used when an unknown/unexpected error has ocurred. Try to avoid over-using this. +var ErrUnknown = errors.New("unknown/unexpected error has occurred") From ff41a90ee5a2d42ba2ebd17b815160e9b67f4b00 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 11:03:39 +0530 Subject: [PATCH 17/51] Added Database Migrations for Handling Blaclisted Tokens --- migrations/1599589830_blacklisted_tokens.down.sql | 1 + migrations/1599589830_blacklisted_tokens.up.sql | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 migrations/1599589830_blacklisted_tokens.down.sql create mode 100644 migrations/1599589830_blacklisted_tokens.up.sql diff --git a/migrations/1599589830_blacklisted_tokens.down.sql b/migrations/1599589830_blacklisted_tokens.down.sql new file mode 100644 index 0000000..fa6445e --- /dev/null +++ b/migrations/1599589830_blacklisted_tokens.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS user_blacklisted_tokens; \ No newline at end of file diff --git a/migrations/1599589830_blacklisted_tokens.up.sql b/migrations/1599589830_blacklisted_tokens.up.sql new file mode 100644 index 0000000..4e6dad1 --- /dev/null +++ b/migrations/1599589830_blacklisted_tokens.up.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS user_blacklisted_tokens( + id SERIAL NOT NULL PRIMARY KEY, + user_id BIGINT REFERENCES users(id), + token TEXT, + expiration_date TIMESTAMP +); \ No newline at end of file From 674437570444cc719af9165a4d23199ec79c186b Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 11:04:32 +0530 Subject: [PATCH 18/51] Created file for managing user_blacklisted_tokens methods --- db/user_blacklisted_tokens.go | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 db/user_blacklisted_tokens.go diff --git a/db/user_blacklisted_tokens.go b/db/user_blacklisted_tokens.go new file mode 100644 index 0000000..432e9a5 --- /dev/null +++ b/db/user_blacklisted_tokens.go @@ -0,0 +1,48 @@ +package db + +import ( + "context" + "fmt" + "time" + + logger "github.com/sirupsen/logrus" +) + +//BlacklistedToken - struct representing a token to be blacklisted (logout) +type BlacklistedToken struct { + ID int `db:"id" json:"id"` + UserID float64 `db:"user_id" json:"user_id"` + Token string `db:"token" json:"token"` + ExpirationDate time.Time `db:"expiration_date" json:"expiration_date"` +} + +const ( + insertBlacklistedToken = `INSERT INTO user_blacklisted_tokens +(user_id, token, expiration_date) +VALUES ($1, $2, $3)` + + selectBlacklistedToken = `SELECT * FROM user_blacklisted_tokens +WHERE token=$1` +) + +func (s *pgStore) CreateBlacklistedToken(ctx context.Context, token BlacklistedToken) (err error) { + _, err = s.db.Exec(insertBlacklistedToken, token.UserID, token.Token, token.ExpirationDate) + + if err != nil { + errMsg := fmt.Sprintf("Error inserting the blacklisted token for user with id %d", token.UserID) + logger.WithField("err", err.Error()).Error(errMsg) + return + } + return +} + +func (s *pgStore) CheckBlacklistedToken(ctx context.Context, token string) bool { + query := fmt.Sprintf("SELECT * FROM user_blacklisted_tokens WHERE token=%s", token) + result, err := s.db.Exec(query) + fmt.Println(result) + if err != nil { + logger.WithField("err", err.Error()).Error("Query Failed") + return false + } + return true +} From 931af3e051644cc55130871fcd01c73c3e1a086e Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 13:06:47 +0530 Subject: [PATCH 19/51] Added Necessary Comments --- config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.go b/config/config.go index 16ef6c1..b2229c2 100644 --- a/config/config.go +++ b/config/config.go @@ -80,6 +80,7 @@ func ReadEnvBool(key string) bool { return viper.GetBool(key) } +//CheckIfSet checks if all the necessary keys are set func checkIfSet(key string) { if !viper.IsSet(key) { err := fmt.Errorf("Key %s is not set", key) From 4f9b70d2cd7dfdaa79c6a6d3be47480da69e4969 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 13:09:49 +0530 Subject: [PATCH 20/51] Updated CreateBlacklistedToken --- db/db.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/db/db.go b/db/db.go index 1bd98e4..385f5c0 100644 --- a/db/db.go +++ b/db/db.go @@ -10,8 +10,5 @@ type Storer interface { AuthenticateUser(context.Context, User) (User, error) GetUser(context.Context, int) (User, error) CreateBlacklistedToken(context.Context, BlacklistedToken) error - CheckBlacklistedToken(context.Context, string) bool - //Create(context.Context, User) error - //GetUser(context.Context) (User, error) - //Delete(context.Context, string) error + CheckBlacklistedToken(context.Context, string) (bool, int) } From 166d88037ad5f7560e117274a1f19f54df93f9b1 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 13:10:53 +0530 Subject: [PATCH 21/51] Added Necessary Comments --- db/user.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/db/user.go b/db/user.go index 04595e9..fde9c57 100644 --- a/db/user.go +++ b/db/user.go @@ -23,6 +23,7 @@ type User struct { CreatedAt string `db:"created_at" json:"created_at"` } +//ListUsers function to fetch all Users From Database func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { err = s.db.Select(&users, "SELECT * FROM users ORDER BY first_name ASC") if err != nil { @@ -33,6 +34,7 @@ func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { return } +//GetUser function is used to Get a Particular User func (s *pgStore) GetUser(ctx context.Context, id int) (user User, err error) { err = s.db.Get(&user, "SELECT * FROM users WHERE id=$1", id) @@ -47,7 +49,8 @@ func (s *pgStore) GetUser(ctx context.Context, id int) (user User, err error) { return } -// GetUserByMobile - Given a mobile number, return that user. +//AuthenticateUser Function checks if User has Registered before Login +// and Has Entered Correct Credentials func (s *pgStore) AuthenticateUser(ctx context.Context, u User) (user User, err error) { err = s.db.Get(&user, "SELECT * FROM users where email = $1", u.Email) @@ -60,6 +63,5 @@ func (s *pgStore) AuthenticateUser(ctx context.Context, u User) (user User, err // If the two passwords don't match, return a 401 status logger.WithField("Error", err.Error()) } - return } From 54d934531f5dc22243758582d4cc31fb0e5ebdb1 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 13:11:57 +0530 Subject: [PATCH 22/51] Updated jwtMiddleware function --- service/router.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/service/router.go b/service/router.go index d9593c4..2b373c0 100644 --- a/service/router.go +++ b/service/router.go @@ -38,17 +38,20 @@ func InitRouter(deps Dependencies) (router *mux.Router) { return } +//jwtMiddleWare function is used to authenticate and authorize the incoming request func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { mySigningKey := config.JWTKey() + //Fetching userID from RequestURL var idParam = mux.Vars(req)["id"] - validID, ok := strconv.Atoi(idParam) + validID, ok := strconv.Atoi(idParam) if ok != nil { logger.Error(ok.Error()) } + authToken := req.Header["Token"] if authToken != nil { token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { @@ -67,10 +70,18 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { } claims, ok := token.Claims.(jwt.MapClaims) - disdis := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]) + //Fetching Status of Token Being Blacklisted or Not + isBlacklisted, _ := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]) + + //Unauthorized User if Token BlackListed + if isBlacklisted { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } - fmt.Println("DIsDis : ", disdis) - if !ok && !token.Valid && !disdis { + //Unauthorized User if Token Invalid + if !ok && !token.Valid { rw.WriteHeader(http.StatusUnauthorized) rw.Write([]byte("Unauthorized")) return @@ -78,6 +89,7 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { userID := claims["id"] + //Unauthorized User if userID in Token Doesn't Match userID in RequestURL if float64(validID) != userID { rw.WriteHeader(http.StatusUnauthorized) rw.Write([]byte("Unauthorized")) From 80cebad5fa1727157db6c0ed6b9440aec4b6612a Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 13:12:47 +0530 Subject: [PATCH 23/51] Added Necessary Comments --- service/user_http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/user_http.go b/service/user_http.go index 4196736..5f0a45e 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -46,7 +46,7 @@ func getUserHandler(deps Dependencies) http.HandlerFunc { var idParam = mux.Vars(req)["id"] id, err := strconv.Atoi(idParam) if err != nil { - logger.Error("COnversion Failed") + logger.Error("Conversion Failed") } user, err := deps.Store.GetUser(req.Context(), id) if err != nil { From f8d75a81a0a99eb844066e108e51e95988f559f7 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 13:14:12 +0530 Subject: [PATCH 24/51] Updated CheckBlacklistedToken Function --- db/user_blacklisted_tokens.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/db/user_blacklisted_tokens.go b/db/user_blacklisted_tokens.go index 432e9a5..4cc9dc2 100644 --- a/db/user_blacklisted_tokens.go +++ b/db/user_blacklisted_tokens.go @@ -20,29 +20,30 @@ const ( insertBlacklistedToken = `INSERT INTO user_blacklisted_tokens (user_id, token, expiration_date) VALUES ($1, $2, $3)` - - selectBlacklistedToken = `SELECT * FROM user_blacklisted_tokens -WHERE token=$1` ) +//CreateBlacklistedToken function to insert the blacklisted token in database func (s *pgStore) CreateBlacklistedToken(ctx context.Context, token BlacklistedToken) (err error) { _, err = s.db.Exec(insertBlacklistedToken, token.UserID, token.Token, token.ExpirationDate) if err != nil { - errMsg := fmt.Sprintf("Error inserting the blacklisted token for user with id %d", token.UserID) + errMsg := fmt.Sprintf("Error inserting the blacklisted token for user with id %v", token.UserID) logger.WithField("err", err.Error()).Error(errMsg) return } return } -func (s *pgStore) CheckBlacklistedToken(ctx context.Context, token string) bool { - query := fmt.Sprintf("SELECT * FROM user_blacklisted_tokens WHERE token=%s", token) - result, err := s.db.Exec(query) - fmt.Println(result) +//CheckBlacklistedToken function to check if token is blacklisted earlier +func (s *pgStore) CheckBlacklistedToken(ctx context.Context, token string) (bool, int) { + + var userID int + query1 := fmt.Sprintf("SELECT user_id FROM user_blacklisted_tokens WHERE token='%s'", token) + err := s.db.QueryRow(query1).Scan(&userID) + if err != nil { - logger.WithField("err", err.Error()).Error("Query Failed") - return false + logger.WithField("err", err.Error()).Error("Either Query Failed or No Rows Found") + return false, -1 } - return true + return true, userID } From 402794174a7568bc480fec2f09b6aa3dcea8fe10 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 15:16:34 +0530 Subject: [PATCH 25/51] Refactored JWTMiddleWare function and added necessary comments --- service/router.go | 84 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/service/router.go b/service/router.go index 2b373c0..2471652 100644 --- a/service/router.go +++ b/service/router.go @@ -30,7 +30,7 @@ func InitRouter(deps Dependencies) (router *mux.Router) { //Router for Get User from ID router.Handle("/user/{id}", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) - //Router for Get User from ID + //Router for User Logout router.Handle("/user/{id}/logout", jwtMiddleWare(userLogoutHandler(deps), deps)).Methods(http.MethodDelete).Headers(versionHeader, v1) //Router for Get All Users @@ -46,57 +46,61 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { //Fetching userID from RequestURL var idParam = mux.Vars(req)["id"] - - validID, ok := strconv.Atoi(idParam) - if ok != nil { - logger.Error(ok.Error()) + validID, err := strconv.Atoi(idParam) + if err != nil { + logger.Error(err.Error()) } authToken := req.Header["Token"] - if authToken != nil { - token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("There was an error") - } - return mySigningKey, nil - }) - if err != nil { - if err == jwt.ErrSignatureInvalid { - rw.WriteHeader(http.StatusUnauthorized) - return - } - rw.WriteHeader(http.StatusBadRequest) - return - } - claims, ok := token.Claims.(jwt.MapClaims) - //Fetching Status of Token Being Blacklisted or Not - isBlacklisted, _ := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]) + //Checking if token not present in header + if authToken == nil { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } - //Unauthorized User if Token BlackListed - if isBlacklisted { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) - return + token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("There was an error") } - - //Unauthorized User if Token Invalid - if !ok && !token.Valid { + return mySigningKey, nil + }) + if err != nil { + if err == jwt.ErrSignatureInvalid { rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) return } + rw.WriteHeader(http.StatusBadRequest) + return + } - userID := claims["id"] + claims, ok := token.Claims.(jwt.MapClaims) - //Unauthorized User if userID in Token Doesn't Match userID in RequestURL - if float64(validID) != userID { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) - return - } + //Fetching Status of Token Being Blacklisted or Not + // Unauthorized User if Token BlackListed + if isBlacklisted, _ := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]); isBlacklisted { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } - endpoint.ServeHTTP(rw, req) + //Unauthorized User if Token Invalid + if !ok && !token.Valid { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return } + + userID := claims["id"] + + //Unauthorized User if userID in Token Doesn't Match userID in RequestURL + if float64(validID) != userID { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } + + endpoint.ServeHTTP(rw, req) }) } From a5e96edc7eba2559816c15480e4430d21f775adc Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 15:17:10 +0530 Subject: [PATCH 26/51] Refactored the userLoginHandle and userLogoutHandle function and added necessary comments --- service/session_http.go | 98 +++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/service/session_http.go b/service/session_http.go index 6161f14..236e899 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -12,6 +12,7 @@ import ( "time" ) +//generateJWT function generates and return a new JWT token func generateJwt(userID int) (tokenString string, err error) { mySigningKey := config.JWTKey() if mySigningKey == nil { @@ -32,15 +33,14 @@ func generateJwt(userID int) (tokenString string, err error) { return } -// @Title userLogin -// @Description Logging User in -// @Router /login [post] -// @Accept json -// @Success 200 {object} -// @Failure 400 {object} +//userLoginHandler function take credentials in json +// and check if the credentials are correct +// also generate and returns a JWT token in the case of correct crendential func userLoginHandler(deps Dependencies) http.HandlerFunc { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { user := db.User{} + + //fetching the json object to get crendentials of users err := json.NewDecoder(req.Body).Decode(&user) if err != nil { logger.WithField("err", err.Error()).Error("JSON Decoding Failed") @@ -48,6 +48,8 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { return } + //checking if the user is authenticate or not + // by passing the credentials to the AuthenticateUser function user, err1 := deps.Store.AuthenticateUser(req.Context(), user) if err1 != nil { logger.WithField("err", err1.Error()).Error("Invalid Credentials") @@ -55,6 +57,8 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { return } + //Generate new JWT token if the user is authenticated + // and return the token in request header token, err := generateJwt(user.ID) if err != nil { ae.Error(ae.ErrUnknown, "Unknown/unexpected error while creating JWT", err) @@ -70,58 +74,68 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { }) } +//userLogoutHandler function logs the user off +// and add the valid JWT token in BlacklistedToken func userLogoutHandler(deps Dependencies) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { mySigningKey := config.JWTKey() + //fetching the token from header authToken := req.Header["Token"] - if authToken != nil { - token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("There was an error") - } - return mySigningKey, nil - }) - if err != nil { - if err == jwt.ErrSignatureInvalid { - rw.WriteHeader(http.StatusUnauthorized) - return - } - rw.WriteHeader(http.StatusBadRequest) - return - } - claims, ok := token.Claims.(jwt.MapClaims) - if !ok && !token.Valid { + //Checking if token not present in header + if authToken == nil { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } + + token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("There was an error") + } + return mySigningKey, nil + }) + if err != nil { + if err == jwt.ErrSignatureInvalid { rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) return } + rw.WriteHeader(http.StatusBadRequest) + return + } - userID := claims["id"].(float64) + claims, ok := token.Claims.(jwt.MapClaims) - if err != nil { - logger.WithField("err", err.Error()).Error("Conversion Failed") - } - expirationTimeStamp := int64(claims["exp"].(float64)) - expirationDate := time.Unix(expirationTimeStamp, 0) + //Checking if token not valid + if !ok && !token.Valid { + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) + return + } - userBlackListedToken := db.BlacklistedToken{ - UserID: userID, - ExpirationDate: expirationDate, - Token: authToken[0], - } + //fetching details from the token + userID := claims["id"].(float64) + expirationTimeStamp := int64(claims["exp"].(float64)) + expirationDate := time.Unix(expirationTimeStamp, 0) + + //create a BlacklistedToken to add in database + // To blacklist a user valid token + userBlackListedToken := db.BlacklistedToken{ + UserID: userID, + ExpirationDate: expirationDate, + Token: authToken[0], + } - err = deps.Store.CreateBlacklistedToken(req.Context(), userBlackListedToken) - if err != nil { - ae.Error(ae.ErrFailedToCreate, "Error creating blaclisted token record", err) - rw.Header().Add("Content-Type", "application/json") - ae.JSONError(rw, http.StatusInternalServerError, err) - return - } + err = deps.Store.CreateBlacklistedToken(req.Context(), userBlackListedToken) + if err != nil { + ae.Error(ae.ErrFailedToCreate, "Error creating blaclisted token record", err) rw.Header().Add("Content-Type", "application/json") + ae.JSONError(rw, http.StatusInternalServerError, err) return } + rw.Header().Add("Content-Type", "application/json") + return }) } From e3152fe659fbc2297fd940bc6660135aa2455030 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 15:17:35 +0530 Subject: [PATCH 27/51] Added necessary comments --- service/user_http.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/service/user_http.go b/service/user_http.go index 5f0a45e..74d363f 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -8,12 +8,8 @@ import ( "strconv" ) -// @Title listUsers -// @Description list all User -// @Router /users [get] -// @Accept json -// @Success 200 {object} -// @Failure 400 {object} +//listUsersHandler function fetch all users from database +// and return as json object func listUsersHandler(deps Dependencies) http.HandlerFunc { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { users, err := deps.Store.ListUsers(req.Context()) @@ -35,19 +31,17 @@ func listUsersHandler(deps Dependencies) http.HandlerFunc { }) } -// @Title getUser -// @Description Show User User -// @Router /user/{id} [get] -// @Accept json -// @Success 200 {object} -// @Failure 400 {object} +//listUsersHandler function fetch specific user from database +// and return as json object func getUserHandler(deps Dependencies) http.HandlerFunc { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + //fetch usedId from request var idParam = mux.Vars(req)["id"] id, err := strconv.Atoi(idParam) if err != nil { logger.Error("Conversion Failed") } + user, err := deps.Store.GetUser(req.Context(), id) if err != nil { logger.WithField("err", err.Error()).Error("Error fetching data") From 8ee4898a9c5fda93c946da3eb12216e0bd0ee7ed Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 16:28:41 +0530 Subject: [PATCH 28/51] Removed Unused Error Messages --- apperrors/apperrors.go | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/apperrors/apperrors.go b/apperrors/apperrors.go index eb927fc..aef80bc 100644 --- a/apperrors/apperrors.go +++ b/apperrors/apperrors.go @@ -21,7 +21,6 @@ package apperrors import ( "encoding/json" "errors" - "fmt" l "github.com/sirupsen/logrus" "net/http" ) @@ -62,11 +61,6 @@ func JSONError(rw http.ResponseWriter, status int, err error) { return } -// ErrKeyNotSet - Returns error object specific to the key value passed in -func ErrKeyNotSet(key string) (err error) { - return fmt.Errorf("Key not set: %s", key) -} - // ErrRecordNotFound - for when a database record isn't found var ErrRecordNotFound = errors.New("Database record not found") @@ -80,26 +74,14 @@ var ErrSignedString = errors.New("Failed to sign token string") // ErrMissingAuthHeader - When the HTTP request doesn't contain an 'Authorization' header var ErrMissingAuthHeader = errors.New("Missing Auth header") -// ErrJSONParseFail - for some reason, the call to json.Unmarshal or json.Marshal returned an error +// ErrJSONParseFail - If json.Unmarshal or json.Marshal returns an error var ErrJSONParseFail = errors.New("Failed to parse JSON response (likely not valid JSON)") -// ErrReadingResponseBody - If for some reason the app can't read the HTTP response body -// issued by another server (used when we try to read user information via oauth during -// login process) -var ErrReadingResponseBody = errors.New("Could not read HTTP response body") - -// ErrHTTPRequestFailed - The HTTP request we issued failed for some reason -var ErrHTTPRequestFailed = errors.New("HTTP Request Failed") - // ErrNoSigningKey - there isn't a signing key defined in the app configuration var ErrNoSigningKey = errors.New("no JWT signing key specified; cannot authenticate users. Define JWT_SECRET in application.yml and restart") -// ErrFailedToCreate - Failed to create record in database +// ErrFailedToCreate - Record Creation Failed var ErrFailedToCreate = errors.New("Failed to create database record") -// ----- -// Let's make the more "generic" errors dead last in our file -// ----- - -// ErrUnknown - Used when an unknown/unexpected error has ocurred. Try to avoid over-using this. +// ErrUnknown - Generic Error For Unknown Errors var ErrUnknown = errors.New("unknown/unexpected error has occurred") From d1ba6e5144434790023711b6e5a861082e3ae731 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 16:30:06 +0530 Subject: [PATCH 29/51] Handled Errors uding Messages From apperrors file --- service/router.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/service/router.go b/service/router.go index 2471652..8025eb8 100644 --- a/service/router.go +++ b/service/router.go @@ -5,6 +5,7 @@ import ( "github.com/dgrijalva/jwt-go" "github.com/gorilla/mux" logger "github.com/sirupsen/logrus" + ae "joshsoftware/go-e-commerce/apperrors" "joshsoftware/go-e-commerce/config" "net/http" "strconv" @@ -55,8 +56,8 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { //Checking if token not present in header if authToken == nil { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) + ae.Error(ae.ErrUnknown, "Unknown/unexpected error while creating JWT", err) + ae.JSONError(rw, http.StatusInternalServerError, err) return } From 97b3619bd4f271ecf6b880423c6e73e0b0fe0109 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 16:31:29 +0530 Subject: [PATCH 30/51] Handled Error Messages from apperrors file --- service/session_http.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/service/session_http.go b/service/session_http.go index 236e899..5c54de1 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -48,12 +48,12 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { return } - //checking if the user is authenticate or not + //checking if the user is authenticated or not // by passing the credentials to the AuthenticateUser function user, err1 := deps.Store.AuthenticateUser(req.Context(), user) if err1 != nil { logger.WithField("err", err1.Error()).Error("Invalid Credentials") - rw.WriteHeader(http.StatusInternalServerError) + rw.WriteHeader(http.StatusUnauthorized) return } @@ -61,14 +61,17 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { // and return the token in request header token, err := generateJwt(user.ID) if err != nil { - ae.Error(ae.ErrUnknown, "Unknown/unexpected error while creating JWT", err) - ae.JSONError(rw, http.StatusInternalServerError, err) + ae.Error(ae.ErrMissingAuthHeader, "Authentication Token in Header Missing", err) + ae.JSONError(rw, http.StatusUnauthorized, err) return } respBytes, err := json.Marshal(user) + if err != nil { + ae.Error(ae.ErrJSONParseFail, "JSON Parsing Failed", err) + } - rw.Header().Add("Authorization", token) + rw.Header().Add("Token", token) rw.Header().Add("Content-Type", "application/json") rw.Write(respBytes) }) @@ -110,8 +113,8 @@ func userLogoutHandler(deps Dependencies) http.Handler { //Checking if token not valid if !ok && !token.Valid { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) + ae.Error(ae.ErrInvalidToken, "Authentication Token Invalid", err) + ae.JSONError(rw, http.StatusUnauthorized, err) return } From 3dbe5a0f0b1e592f3a710bb40fa2622209ce7350 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 16:36:00 +0530 Subject: [PATCH 31/51] Changes in Commenting --- apperrors/apperrors.go | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/apperrors/apperrors.go b/apperrors/apperrors.go index aef80bc..a832ffb 100644 --- a/apperrors/apperrors.go +++ b/apperrors/apperrors.go @@ -1,21 +1,3 @@ -/* -======================================================================== -Error Definitions - This file, part of the "apperrors" package, defines all the error - variables by name that'll be used throughout the entire application. -Rules - Every error variable MUST start with "Err" - with a capital E so - we can export it for use in other packages. - Additionally, please place a comment (one or more lines) on the line - before the declaration of the error variable that explains what that - variable is intended to be used for, or the error concept it's meant - to represent. -Notes - There are also additional helper functions in this file pertaining to - miscellaneous error handling. -======================================================================== -*/ - package apperrors import ( @@ -25,11 +7,11 @@ import ( "net/http" ) -// ErrorStruct - a generic struct you can use to create error messages/logs to be converted -// to JSON or other types of messages/data as you need it +// ErrorStruct - struct used to convert error messages into required JSON format + type ErrorStruct struct { - Message string `json:"message,omitempty"` // Your message to the end user or developer - Status int `json:"status,omitempty"` // HTTP status code that should go with the message/log (if any) + Message string `json:"message,omitempty"` //Error Message + Status int `json:"status,omitempty"` //HTTP Response status code } // Error - prints out an error @@ -45,7 +27,7 @@ func Warn(appError error, msg string, triggeringError error) { // JSONError - This function writes out an error response with the status // header passed in func JSONError(rw http.ResponseWriter, status int, err error) { - // Create the ErrorStruct object for later use + errObj := ErrorStruct{ Message: err.Error(), Status: status, From 6fb9221e6ecd2db2816949f366fe187a25771f02 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 16:36:53 +0530 Subject: [PATCH 32/51] Changes in Commenting --- apperrors/apperrors.go | 1 - 1 file changed, 1 deletion(-) diff --git a/apperrors/apperrors.go b/apperrors/apperrors.go index a832ffb..83f0869 100644 --- a/apperrors/apperrors.go +++ b/apperrors/apperrors.go @@ -8,7 +8,6 @@ import ( ) // ErrorStruct - struct used to convert error messages into required JSON format - type ErrorStruct struct { Message string `json:"message,omitempty"` //Error Message Status int `json:"status,omitempty"` //HTTP Response status code From 08589e069fcaf36b07ba27efcf15f4fe8a0c0d96 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 20:38:31 +0530 Subject: [PATCH 33/51] Changes in Error Handling for GenerateJWTToken --- service/session_http.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/session_http.go b/service/session_http.go index 5c54de1..f501202 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -61,8 +61,8 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { // and return the token in request header token, err := generateJwt(user.ID) if err != nil { - ae.Error(ae.ErrMissingAuthHeader, "Authentication Token in Header Missing", err) - ae.JSONError(rw, http.StatusUnauthorized, err) + rw.WriteHeader(http.StatusInternalServerError) + rw.Write([]byte("Token Generation Failure")) return } From 340650c39f2ace0892494a6aadfc01b75c322376 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Wed, 9 Sep 2020 20:40:06 +0530 Subject: [PATCH 34/51] Removed NOT NULL constraint from Mobile from User --- migrations/1587381324_create_users.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/1587381324_create_users.up.sql b/migrations/1587381324_create_users.up.sql index 65f44a2..65bba52 100644 --- a/migrations/1587381324_create_users.up.sql +++ b/migrations/1587381324_create_users.up.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS users ( first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, - mobile VARCHAR(20) NOT NULL UNIQUE, + mobile VARCHAR(20) UNIQUE, country VARCHAR(100), state VARCHAR(100), city VARCHAR(100), From 8d13a62b18cd0cf26458b5fb4941a6c8c5b6721b Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Thu, 10 Sep 2020 01:11:17 +0530 Subject: [PATCH 35/51] Added Regex for id validation in routes --- service/router.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/router.go b/service/router.go index 8025eb8..62082e4 100644 --- a/service/router.go +++ b/service/router.go @@ -29,10 +29,10 @@ func InitRouter(deps Dependencies) (router *mux.Router) { router.HandleFunc("/login", userLoginHandler(deps)).Methods(http.MethodPost).Headers(versionHeader, v1) //Router for Get User from ID - router.Handle("/user/{id}", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) + router.Handle("/user/{id:[0-9]+}", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) //Router for User Logout - router.Handle("/user/{id}/logout", jwtMiddleWare(userLogoutHandler(deps), deps)).Methods(http.MethodDelete).Headers(versionHeader, v1) + router.Handle("/user/{id:[0-9]+}/logout", jwtMiddleWare(userLogoutHandler(deps), deps)).Methods(http.MethodDelete).Headers(versionHeader, v1) //Router for Get All Users router.HandleFunc("/users", listUsersHandler(deps)).Methods(http.MethodGet).Headers(versionHeader, v1) From 944ea05b14fb16e35ea3dbc62512db6de200bcf5 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Thu, 10 Sep 2020 01:17:07 +0530 Subject: [PATCH 36/51] Added Necessary Error Handling --- service/user_http.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/service/user_http.go b/service/user_http.go index 74d363f..5012f84 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -37,9 +37,13 @@ func getUserHandler(deps Dependencies) http.HandlerFunc { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { //fetch usedId from request var idParam = mux.Vars(req)["id"] + id, err := strconv.Atoi(idParam) + if err != nil { - logger.Error("Conversion Failed") + logger.WithField("err", err.Error()).Error("Invalid User ID") + rw.WriteHeader(http.StatusBadRequest) + return } user, err := deps.Store.GetUser(req.Context(), id) From c2073ab37d9675f2fdff5e8a4bb9105ad084090a Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Thu, 10 Sep 2020 12:13:27 +0530 Subject: [PATCH 37/51] Added necessary Error Handling --- service/router.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/service/router.go b/service/router.go index 62082e4..950fa63 100644 --- a/service/router.go +++ b/service/router.go @@ -2,13 +2,14 @@ package service import ( "fmt" - "github.com/dgrijalva/jwt-go" - "github.com/gorilla/mux" - logger "github.com/sirupsen/logrus" ae "joshsoftware/go-e-commerce/apperrors" "joshsoftware/go-e-commerce/config" "net/http" "strconv" + + "github.com/dgrijalva/jwt-go" + "github.com/gorilla/mux" + logger "github.com/sirupsen/logrus" ) const ( @@ -29,7 +30,7 @@ func InitRouter(deps Dependencies) (router *mux.Router) { router.HandleFunc("/login", userLoginHandler(deps)).Methods(http.MethodPost).Headers(versionHeader, v1) //Router for Get User from ID - router.Handle("/user/{id:[0-9]+}", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) + router.Handle("/user/{id}", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) //Router for User Logout router.Handle("/user/{id:[0-9]+}/logout", jwtMiddleWare(userLogoutHandler(deps), deps)).Methods(http.MethodDelete).Headers(versionHeader, v1) @@ -49,15 +50,17 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { var idParam = mux.Vars(req)["id"] validID, err := strconv.Atoi(idParam) if err != nil { - logger.Error(err.Error()) + logger.WithField("err", err.Error()).Error("Invalid User ID") + rw.WriteHeader(http.StatusBadRequest) + return } authToken := req.Header["Token"] //Checking if token not present in header - if authToken == nil { - ae.Error(ae.ErrUnknown, "Unknown/unexpected error while creating JWT", err) - ae.JSONError(rw, http.StatusInternalServerError, err) + if len(authToken[0]) < 1 { + ae.Error(ae.ErrMissingAuthHeader, "Missing Authentication Token From Header", err) + rw.WriteHeader(http.StatusUnauthorized) return } From b36411e2006758fbf64ef8b21e0ede8e6719016a Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Thu, 10 Sep 2020 12:15:50 +0530 Subject: [PATCH 38/51] Added Json responce object as AuthToken --- service/session_http.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/service/session_http.go b/service/session_http.go index f501202..fd11ef9 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -3,15 +3,22 @@ package service import ( "encoding/json" "fmt" - "github.com/dgrijalva/jwt-go" - logger "github.com/sirupsen/logrus" ae "joshsoftware/go-e-commerce/apperrors" "joshsoftware/go-e-commerce/config" "joshsoftware/go-e-commerce/db" "net/http" "time" + + "github.com/dgrijalva/jwt-go" + logger "github.com/sirupsen/logrus" ) +//AuthBody stores responce body for login +type AuthBody struct { + Message string `json:"meassage"` + Token string `json:"token"` +} + //generateJWT function generates and return a new JWT token func generateJwt(userID int) (tokenString string, err error) { mySigningKey := config.JWTKey() @@ -48,6 +55,7 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { return } + //TODO change no need to return user object from Authentication //checking if the user is authenticated or not // by passing the credentials to the AuthenticateUser function user, err1 := deps.Store.AuthenticateUser(req.Context(), user) @@ -65,13 +73,16 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { rw.Write([]byte("Token Generation Failure")) return } + authbody := AuthBody{ + Message: "Login Successfull", + Token: token, + } - respBytes, err := json.Marshal(user) + respBytes, err := json.Marshal(authbody) if err != nil { ae.Error(ae.ErrJSONParseFail, "JSON Parsing Failed", err) } - rw.Header().Add("Token", token) rw.Header().Add("Content-Type", "application/json") rw.Write(respBytes) }) @@ -138,7 +149,7 @@ func userLogoutHandler(deps Dependencies) http.Handler { ae.JSONError(rw, http.StatusInternalServerError, err) return } - rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(http.StatusOK) return }) } From 9e705dda3e2be863fd61b32be995881faabc92be Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Thu, 10 Sep 2020 12:20:02 +0530 Subject: [PATCH 39/51] Updated constraint from user migration --- migrations/1587381324_create_users.up.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/1587381324_create_users.up.sql b/migrations/1587381324_create_users.up.sql index 65bba52..75c0b9a 100644 --- a/migrations/1587381324_create_users.up.sql +++ b/migrations/1587381324_create_users.up.sql @@ -1,9 +1,9 @@ CREATE TABLE IF NOT EXISTS users ( id SERIAL NOT NULL PRIMARY KEY, first_name VARCHAR(255) NOT NULL, - last_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255), email VARCHAR(255) NOT NULL UNIQUE, - mobile VARCHAR(20) UNIQUE, + mobile VARCHAR(20), country VARCHAR(100), state VARCHAR(100), city VARCHAR(100), From c8c57588903959941f76c1d1a47ed77b24423db4 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Thu, 10 Sep 2020 16:43:57 +0530 Subject: [PATCH 40/51] Changes in route and jwtMiddleWare function --- service/router.go | 104 +++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/service/router.go b/service/router.go index 950fa63..13f7818 100644 --- a/service/router.go +++ b/service/router.go @@ -2,14 +2,9 @@ package service import ( "fmt" - ae "joshsoftware/go-e-commerce/apperrors" + "github.com/gorilla/mux" "joshsoftware/go-e-commerce/config" "net/http" - "strconv" - - "github.com/dgrijalva/jwt-go" - "github.com/gorilla/mux" - logger "github.com/sirupsen/logrus" ) const ( @@ -30,10 +25,10 @@ func InitRouter(deps Dependencies) (router *mux.Router) { router.HandleFunc("/login", userLoginHandler(deps)).Methods(http.MethodPost).Headers(versionHeader, v1) //Router for Get User from ID - router.Handle("/user/{id}", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) + router.Handle("/user", jwtMiddleWare(getUserHandler(deps), deps)).Methods(http.MethodGet).Headers(versionHeader, v1) //Router for User Logout - router.Handle("/user/{id:[0-9]+}/logout", jwtMiddleWare(userLogoutHandler(deps), deps)).Methods(http.MethodDelete).Headers(versionHeader, v1) + router.Handle("/logout", jwtMiddleWare(userLogoutHandler(deps), deps)).Methods(http.MethodDelete).Headers(versionHeader, v1) //Router for Get All Users router.HandleFunc("/users", listUsersHandler(deps)).Methods(http.MethodGet).Headers(versionHeader, v1) @@ -44,42 +39,49 @@ func InitRouter(deps Dependencies) (router *mux.Router) { func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - mySigningKey := config.JWTKey() + // mySigningKey := config.JWTKey() - //Fetching userID from RequestURL - var idParam = mux.Vars(req)["id"] - validID, err := strconv.Atoi(idParam) - if err != nil { - logger.WithField("err", err.Error()).Error("Invalid User ID") - rw.WriteHeader(http.StatusBadRequest) - return - } + // //Fetching userID from RequestURL + // var idParam = mux.Vars(req)["id"] + // validID, err := strconv.Atoi(idParam) + // if err != nil { + // logger.WithField("err", err.Error()).Error("Invalid User ID") + // rw.WriteHeader(http.StatusBadRequest) + // return + // } authToken := req.Header["Token"] - //Checking if token not present in header - if len(authToken[0]) < 1 { - ae.Error(ae.ErrMissingAuthHeader, "Missing Authentication Token From Header", err) - rw.WriteHeader(http.StatusUnauthorized) - return - } - - token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("There was an error") - } - return mySigningKey, nil - }) + _, _, err := getDataFromToken(authToken[0]) if err != nil { - if err == jwt.ErrSignatureInvalid { - rw.WriteHeader(http.StatusUnauthorized) - return - } - rw.WriteHeader(http.StatusBadRequest) + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) return } - claims, ok := token.Claims.(jwt.MapClaims) + // //Checking if token not present in header + // if len(authToken[0]) < 1 { + // ae.Error(ae.ErrMissingAuthHeader, "Missing Authentication Token From Header", err) + // rw.WriteHeader(http.StatusUnauthorized) + // return + // } + + // token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { + // if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + // return nil, fmt.Errorf("There was an error") + // } + // return mySigningKey, nil + // }) + // if err != nil { + // if err == jwt.ErrSignatureInvalid { + // rw.WriteHeader(http.StatusUnauthorized) + // return + // } + // rw.WriteHeader(http.StatusBadRequest) + // return + // } + + // claims, ok := token.Claims.(jwt.MapClaims) //Fetching Status of Token Being Blacklisted or Not // Unauthorized User if Token BlackListed @@ -89,21 +91,21 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { return } - //Unauthorized User if Token Invalid - if !ok && !token.Valid { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) - return - } - - userID := claims["id"] - - //Unauthorized User if userID in Token Doesn't Match userID in RequestURL - if float64(validID) != userID { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) - return - } + // //Unauthorized User if Token Invalid + // if !ok && !token.Valid { + // rw.WriteHeader(http.StatusUnauthorized) + // rw.Write([]byte("Unauthorized")) + // return + // } + + // userID := claims["id"] + + // //Unauthorized User if userID in Token Doesn't Match userID in RequestURL + // if float64(validID) != userID { + // rw.WriteHeader(http.StatusUnauthorized) + // rw.Write([]byte("Unauthorized")) + // return + // } endpoint.ServeHTTP(rw, req) }) From 84eebc6bfff535411b9c929714471577387fb312 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Thu, 10 Sep 2020 16:48:48 +0530 Subject: [PATCH 41/51] Added getDataFromToken function and updated userLogoutHandler --- service/session_http.go | 69 ++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/service/session_http.go b/service/session_http.go index fd11ef9..9915005 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -93,45 +93,16 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { func userLogoutHandler(deps Dependencies) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - mySigningKey := config.JWTKey() - //fetching the token from header authToken := req.Header["Token"] - //Checking if token not present in header - if authToken == nil { + //fetching details from the token + userID, expirationTimeStamp, err := getDataFromToken(authToken[0]) + if err != nil { rw.WriteHeader(http.StatusUnauthorized) rw.Write([]byte("Unauthorized")) return } - - token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("There was an error") - } - return mySigningKey, nil - }) - if err != nil { - if err == jwt.ErrSignatureInvalid { - rw.WriteHeader(http.StatusUnauthorized) - return - } - rw.WriteHeader(http.StatusBadRequest) - return - } - - claims, ok := token.Claims.(jwt.MapClaims) - - //Checking if token not valid - if !ok && !token.Valid { - ae.Error(ae.ErrInvalidToken, "Authentication Token Invalid", err) - ae.JSONError(rw, http.StatusUnauthorized, err) - return - } - - //fetching details from the token - userID := claims["id"].(float64) - expirationTimeStamp := int64(claims["exp"].(float64)) expirationDate := time.Unix(expirationTimeStamp, 0) //create a BlacklistedToken to add in database @@ -149,7 +120,41 @@ func userLogoutHandler(deps Dependencies) http.Handler { ae.JSONError(rw, http.StatusInternalServerError, err) return } + + rw.Write([]byte("Logged Out Successfully")) rw.WriteHeader(http.StatusOK) return }) } + +func getDataFromToken(Token string) (userID float64, expirationTime int64, err error) { + mySigningKey := config.JWTKey() + + //Checking if token not present in header + if len(Token) < 1 { + ae.Error(ae.ErrMissingAuthHeader, "Missing Authentication Token From Header", err) + return + } + + token, err := jwt.Parse(Token, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("There was an error") + } + return mySigningKey, nil + }) + if err != nil { + ae.Error(ae.ErrInvalidToken, "Invalid Token", err) + return + } + + claims, ok := token.Claims.(jwt.MapClaims) + + if !ok && !token.Valid { + ae.Error(ae.ErrInvalidToken, "Invalid Token", err) + return + } + + userID = claims["id"].(float64) + expirationTime = int64(claims["exp"].(float64)) + return +} From faa0d43cd53f837b410fd34cb113fb0b84613a08 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Thu, 10 Sep 2020 16:51:30 +0530 Subject: [PATCH 42/51] Updated user_id fteching in getUserHandler function --- service/user_http.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/service/user_http.go b/service/user_http.go index 5012f84..f564a7c 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -2,10 +2,8 @@ package service import ( "encoding/json" - "github.com/gorilla/mux" logger "github.com/sirupsen/logrus" "net/http" - "strconv" ) //listUsersHandler function fetch all users from database @@ -36,18 +34,16 @@ func listUsersHandler(deps Dependencies) http.HandlerFunc { func getUserHandler(deps Dependencies) http.HandlerFunc { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { //fetch usedId from request - var idParam = mux.Vars(req)["id"] - - id, err := strconv.Atoi(idParam) - + authToken := req.Header["Token"] + userID, _, err := getDataFromToken(authToken[0]) if err != nil { - logger.WithField("err", err.Error()).Error("Invalid User ID") - rw.WriteHeader(http.StatusBadRequest) + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte("Unauthorized")) return } - user, err := deps.Store.GetUser(req.Context(), id) - if err != nil { + user, err1 := deps.Store.GetUser(req.Context(), int(userID)) + if err1 != nil { logger.WithField("err", err.Error()).Error("Error fetching data") rw.WriteHeader(http.StatusInternalServerError) return From 9319a24ed4a1c61c6a1773d55fad886ef8805c55 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Thu, 10 Sep 2020 17:47:36 +0530 Subject: [PATCH 43/51] Removed Unnecessary Comments --- service/router.go | 52 ----------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/service/router.go b/service/router.go index 13f7818..4a21e8c 100644 --- a/service/router.go +++ b/service/router.go @@ -39,17 +39,6 @@ func InitRouter(deps Dependencies) (router *mux.Router) { func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - // mySigningKey := config.JWTKey() - - // //Fetching userID from RequestURL - // var idParam = mux.Vars(req)["id"] - // validID, err := strconv.Atoi(idParam) - // if err != nil { - // logger.WithField("err", err.Error()).Error("Invalid User ID") - // rw.WriteHeader(http.StatusBadRequest) - // return - // } - authToken := req.Header["Token"] _, _, err := getDataFromToken(authToken[0]) @@ -59,30 +48,6 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { return } - // //Checking if token not present in header - // if len(authToken[0]) < 1 { - // ae.Error(ae.ErrMissingAuthHeader, "Missing Authentication Token From Header", err) - // rw.WriteHeader(http.StatusUnauthorized) - // return - // } - - // token, err := jwt.Parse(authToken[0], func(token *jwt.Token) (interface{}, error) { - // if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - // return nil, fmt.Errorf("There was an error") - // } - // return mySigningKey, nil - // }) - // if err != nil { - // if err == jwt.ErrSignatureInvalid { - // rw.WriteHeader(http.StatusUnauthorized) - // return - // } - // rw.WriteHeader(http.StatusBadRequest) - // return - // } - - // claims, ok := token.Claims.(jwt.MapClaims) - //Fetching Status of Token Being Blacklisted or Not // Unauthorized User if Token BlackListed if isBlacklisted, _ := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]); isBlacklisted { @@ -90,23 +55,6 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { rw.Write([]byte("Unauthorized")) return } - - // //Unauthorized User if Token Invalid - // if !ok && !token.Valid { - // rw.WriteHeader(http.StatusUnauthorized) - // rw.Write([]byte("Unauthorized")) - // return - // } - - // userID := claims["id"] - - // //Unauthorized User if userID in Token Doesn't Match userID in RequestURL - // if float64(validID) != userID { - // rw.WriteHeader(http.StatusUnauthorized) - // rw.Write([]byte("Unauthorized")) - // return - // } - endpoint.ServeHTTP(rw, req) }) } From cbb000f69187bea00f32da1e6e90d642f65debb2 Mon Sep 17 00:00:00 2001 From: sagar23sj Date: Fri, 11 Sep 2020 00:33:50 +0530 Subject: [PATCH 44/51] Handled CORS --- go.mod | 1 + go.sum | 2 ++ main.go | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/go.mod b/go.mod index f725b07..4a5c631 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.8.0 github.com/mattes/migrate v3.0.1+incompatible + github.com/rs/cors v1.7.0 github.com/sirupsen/logrus v1.6.0 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index 73606e8..5949fe2 100644 --- a/go.sum +++ b/go.sum @@ -155,6 +155,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= diff --git a/main.go b/main.go index e0ad81d..9f9a23a 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( "fmt" + "github.com/rs/cors" "joshsoftware/go-e-commerce/config" "joshsoftware/go-e-commerce/db" "joshsoftware/go-e-commerce/service" @@ -70,6 +71,13 @@ func startApp() (err error) { return } + c := cors.New(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"POST", "GET", "DELETE", "PUT", "PATCH", "OPTIONS"}, + AllowCredentials: true, + Debug: true, + }) + deps := service.Dependencies{ Store: store, } @@ -79,6 +87,7 @@ func startApp() (err error) { // init web server server := negroni.Classic() + server.Use(c) server.UseHandler(router) port := config.AppPort() // This can be changed to the service port number via environment variable. From 69de9e386981d71710ccb6a21a6f27873a0ab82b Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Fri, 11 Sep 2020 14:18:39 +0530 Subject: [PATCH 45/51] Removed Unnecessary Error Handling --- db/user_blacklisted_tokens.go | 1 - 1 file changed, 1 deletion(-) diff --git a/db/user_blacklisted_tokens.go b/db/user_blacklisted_tokens.go index 4cc9dc2..99d80d6 100644 --- a/db/user_blacklisted_tokens.go +++ b/db/user_blacklisted_tokens.go @@ -42,7 +42,6 @@ func (s *pgStore) CheckBlacklistedToken(ctx context.Context, token string) (bool err := s.db.QueryRow(query1).Scan(&userID) if err != nil { - logger.WithField("err", err.Error()).Error("Either Query Failed or No Rows Found") return false, -1 } return true, userID From 3f0adae35c0addecdfe02a6190dc49753f7d8263 Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Fri, 11 Sep 2020 15:02:37 +0530 Subject: [PATCH 46/51] Updated Response Body Structure --- service/responce.go | 39 ++++++++++++++++++++++++++ service/router.go | 14 +++++++--- service/session_http.go | 61 +++++++++++++++++++++++++---------------- service/user_http.go | 44 ++++++++++++++--------------- 4 files changed, 108 insertions(+), 50 deletions(-) create mode 100644 service/responce.go diff --git a/service/responce.go b/service/responce.go new file mode 100644 index 0000000..adb05ec --- /dev/null +++ b/service/responce.go @@ -0,0 +1,39 @@ +package service + +import ( + "encoding/json" + "net/http" + + logger "github.com/sirupsen/logrus" +) + +type successResponse struct { + Data interface{} `json:"data"` +} + +type errorResponse struct { + Error interface{} `json:"error"` +} + +type messageObject struct { + Message string `json:"message"` +} + +type errorObject struct { + Code string `json:"code"` + messageObject + Fields map[string]string `json:"fields"` +} + +func responses(rw http.ResponseWriter, status int, responseBody interface{}) { + respBytes, err := json.Marshal(responseBody) + if err != nil { + logger.WithField("err", err.Error()).Error("Error while marshaling core values data") + rw.WriteHeader(http.StatusInternalServerError) + return + } + + rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(status) + rw.Write(respBytes) +} diff --git a/service/router.go b/service/router.go index 4a21e8c..4d6e9b1 100644 --- a/service/router.go +++ b/service/router.go @@ -43,16 +43,22 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { _, _, err := getDataFromToken(authToken[0]) if err != nil { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) + responses(rw, http.StatusUnauthorized, errorResponse{ + Error: messageObject{ + Message: "Unauthorized User", + }, + }) return } //Fetching Status of Token Being Blacklisted or Not // Unauthorized User if Token BlackListed if isBlacklisted, _ := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]); isBlacklisted { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) + responses(rw, http.StatusUnauthorized, errorResponse{ + Error: messageObject{ + Message: "Unauthorized User", + }, + }) return } endpoint.ServeHTTP(rw, req) diff --git a/service/session_http.go b/service/session_http.go index 9915005..3ec8ff8 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -14,7 +14,7 @@ import ( ) //AuthBody stores responce body for login -type AuthBody struct { +type authBody struct { Message string `json:"meassage"` Token string `json:"token"` } @@ -51,7 +51,12 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { err := json.NewDecoder(req.Body).Decode(&user) if err != nil { logger.WithField("err", err.Error()).Error("JSON Decoding Failed") - rw.WriteHeader(http.StatusBadRequest) + responses(rw, http.StatusBadRequest, errorResponse{ + Error: messageObject{ + Message: "JSON Decoding Failed", + }, + }) + return } @@ -61,7 +66,11 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { user, err1 := deps.Store.AuthenticateUser(req.Context(), user) if err1 != nil { logger.WithField("err", err1.Error()).Error("Invalid Credentials") - rw.WriteHeader(http.StatusUnauthorized) + responses(rw, http.StatusUnauthorized, errorResponse{ + Error: messageObject{ + Message: "Invalid Credentials", + }, + }) return } @@ -69,22 +78,20 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { // and return the token in request header token, err := generateJwt(user.ID) if err != nil { - rw.WriteHeader(http.StatusInternalServerError) - rw.Write([]byte("Token Generation Failure")) + responses(rw, http.StatusInternalServerError, errorResponse{ + Error: messageObject{ + Message: "Token Generation Failure", + }, + }) return } - authbody := AuthBody{ - Message: "Login Successfull", - Token: token, - } - - respBytes, err := json.Marshal(authbody) - if err != nil { - ae.Error(ae.ErrJSONParseFail, "JSON Parsing Failed", err) - } - rw.Header().Add("Content-Type", "application/json") - rw.Write(respBytes) + responses(rw, http.StatusOK, successResponse{ + Data: authBody{ + Message: "Login Successfull", + Token: token, + }, + }) }) } @@ -99,8 +106,11 @@ func userLogoutHandler(deps Dependencies) http.Handler { //fetching details from the token userID, expirationTimeStamp, err := getDataFromToken(authToken[0]) if err != nil { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) + responses(rw, http.StatusUnauthorized, errorResponse{ + Error: messageObject{ + Message: "Unauthorized User", + }, + }) return } expirationDate := time.Unix(expirationTimeStamp, 0) @@ -116,13 +126,18 @@ func userLogoutHandler(deps Dependencies) http.Handler { err = deps.Store.CreateBlacklistedToken(req.Context(), userBlackListedToken) if err != nil { ae.Error(ae.ErrFailedToCreate, "Error creating blaclisted token record", err) - rw.Header().Add("Content-Type", "application/json") - ae.JSONError(rw, http.StatusInternalServerError, err) + responses(rw, http.StatusInternalServerError, errorResponse{ + Error: messageObject{ + Message: "Internal Server Error", + }, + }) return } - - rw.Write([]byte("Logged Out Successfully")) - rw.WriteHeader(http.StatusOK) + responses(rw, http.StatusOK, successResponse{ + Data: messageObject{ + Message: "Logged Out Successfully", + }, + }) return }) } diff --git a/service/user_http.go b/service/user_http.go index f564a7c..1b1c094 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -1,7 +1,6 @@ package service import ( - "encoding/json" logger "github.com/sirupsen/logrus" "net/http" ) @@ -13,19 +12,17 @@ func listUsersHandler(deps Dependencies) http.HandlerFunc { users, err := deps.Store.ListUsers(req.Context()) if err != nil { logger.WithField("err", err.Error()).Error("Error fetching data") - rw.WriteHeader(http.StatusInternalServerError) + responses(rw, http.StatusInternalServerError, errorResponse{ + Error: messageObject{ + Message: "Internal Server Error", + }, + }) return } - respBytes, err := json.Marshal(users) - if err != nil { - logger.WithField("err", err.Error()).Error("Error marshaling users data") - rw.WriteHeader(http.StatusInternalServerError) - return - } - - rw.Header().Add("Content-Type", "application/json") - rw.Write(respBytes) + responses(rw, http.StatusOK, successResponse{ + Data: users, + }) }) } @@ -37,26 +34,27 @@ func getUserHandler(deps Dependencies) http.HandlerFunc { authToken := req.Header["Token"] userID, _, err := getDataFromToken(authToken[0]) if err != nil { - rw.WriteHeader(http.StatusUnauthorized) - rw.Write([]byte("Unauthorized")) + responses(rw, http.StatusUnauthorized, errorResponse{ + Error: messageObject{ + Message: "Unauthorized User", + }, + }) return } user, err1 := deps.Store.GetUser(req.Context(), int(userID)) if err1 != nil { logger.WithField("err", err.Error()).Error("Error fetching data") - rw.WriteHeader(http.StatusInternalServerError) - return - } - - respBytes, err := json.Marshal(user) - if err != nil { - logger.WithField("err", err.Error()).Error("Error marshaling users data") - rw.WriteHeader(http.StatusInternalServerError) + responses(rw, http.StatusInternalServerError, errorResponse{ + Error: messageObject{ + Message: "Internal Server Error", + }, + }) return } - rw.Header().Add("Content-Type", "application/json") - rw.Write(respBytes) + responses(rw, http.StatusOK, successResponse{ + Data: user, + }) }) } From 5c1a4ec73481a3dfe13015e9bfaefd618831725e Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Fri, 11 Sep 2020 15:19:13 +0530 Subject: [PATCH 47/51] Fixed minor error --- service/user_http.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/service/user_http.go b/service/user_http.go index 1b1c094..272e5f0 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -2,6 +2,7 @@ package service import ( logger "github.com/sirupsen/logrus" + "joshsoftware/go-e-commerce/db" "net/http" ) @@ -42,8 +43,9 @@ func getUserHandler(deps Dependencies) http.HandlerFunc { return } - user, err1 := deps.Store.GetUser(req.Context(), int(userID)) - if err1 != nil { + user := db.User{} + user, err = deps.Store.GetUser(req.Context(), int(userID)) + if err != nil { logger.WithField("err", err.Error()).Error("Error fetching data") responses(rw, http.StatusInternalServerError, errorResponse{ Error: messageObject{ From 8545a3c100dbc94e05433652d03618e4d8e4afb6 Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Fri, 11 Sep 2020 15:40:09 +0530 Subject: [PATCH 48/51] Updated Setter And Getter Method For Header --- apperrors/apperrors.go | 2 +- service/ping_http.go | 2 +- service/responce.go | 2 +- service/router.go | 6 +++--- service/session_http.go | 6 +++--- service/user_http.go | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apperrors/apperrors.go b/apperrors/apperrors.go index 83f0869..2704665 100644 --- a/apperrors/apperrors.go +++ b/apperrors/apperrors.go @@ -37,7 +37,7 @@ func JSONError(rw http.ResponseWriter, status int, err error) { Warn(err, "Error in AppErrors marshalling JSON", err) } rw.WriteHeader(status) - rw.Header().Add("Content-Type", "application/json") + rw.Header().Set("Content-Type", "application/json") rw.Write(errJSON) return } diff --git a/service/ping_http.go b/service/ping_http.go index db0743f..3689ac6 100644 --- a/service/ping_http.go +++ b/service/ping_http.go @@ -21,6 +21,6 @@ func pingHandler(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusInternalServerError) } - rw.Header().Add("Content-Type", "application/json") + rw.Header().Set("Content-Type", "application/json") rw.Write(respBytes) } diff --git a/service/responce.go b/service/responce.go index adb05ec..ea62096 100644 --- a/service/responce.go +++ b/service/responce.go @@ -33,7 +33,7 @@ func responses(rw http.ResponseWriter, status int, responseBody interface{}) { return } - rw.Header().Add("Content-Type", "application/json") + rw.Header().Set("Content-Type", "application/json") rw.WriteHeader(status) rw.Write(respBytes) } diff --git a/service/router.go b/service/router.go index 4d6e9b1..47e9aac 100644 --- a/service/router.go +++ b/service/router.go @@ -39,9 +39,9 @@ func InitRouter(deps Dependencies) (router *mux.Router) { func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - authToken := req.Header["Token"] + authToken := req.Header.Get("Token") - _, _, err := getDataFromToken(authToken[0]) + _, _, err := getDataFromToken(authToken) if err != nil { responses(rw, http.StatusUnauthorized, errorResponse{ Error: messageObject{ @@ -53,7 +53,7 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { //Fetching Status of Token Being Blacklisted or Not // Unauthorized User if Token BlackListed - if isBlacklisted, _ := deps.Store.CheckBlacklistedToken(req.Context(), authToken[0]); isBlacklisted { + if isBlacklisted, _ := deps.Store.CheckBlacklistedToken(req.Context(), authToken); isBlacklisted { responses(rw, http.StatusUnauthorized, errorResponse{ Error: messageObject{ Message: "Unauthorized User", diff --git a/service/session_http.go b/service/session_http.go index 3ec8ff8..86c7ca3 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -101,10 +101,10 @@ func userLogoutHandler(deps Dependencies) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { //fetching the token from header - authToken := req.Header["Token"] + authToken := req.Header.Get("Token") //fetching details from the token - userID, expirationTimeStamp, err := getDataFromToken(authToken[0]) + userID, expirationTimeStamp, err := getDataFromToken(authToken) if err != nil { responses(rw, http.StatusUnauthorized, errorResponse{ Error: messageObject{ @@ -120,7 +120,7 @@ func userLogoutHandler(deps Dependencies) http.Handler { userBlackListedToken := db.BlacklistedToken{ UserID: userID, ExpirationDate: expirationDate, - Token: authToken[0], + Token: authToken, } err = deps.Store.CreateBlacklistedToken(req.Context(), userBlackListedToken) diff --git a/service/user_http.go b/service/user_http.go index 272e5f0..7638e58 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -32,8 +32,8 @@ func listUsersHandler(deps Dependencies) http.HandlerFunc { func getUserHandler(deps Dependencies) http.HandlerFunc { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { //fetch usedId from request - authToken := req.Header["Token"] - userID, _, err := getDataFromToken(authToken[0]) + authToken := req.Header.Get("Token") + userID, _, err := getDataFromToken(authToken) if err != nil { responses(rw, http.StatusUnauthorized, errorResponse{ Error: messageObject{ From 6e7a6f94f71e814189c8ec0a10d3582e2dfbd032 Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Fri, 11 Sep 2020 16:36:09 +0530 Subject: [PATCH 49/51] Updated User struct --- db/user.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/db/user.go b/db/user.go index fde9c57..5a5fe07 100644 --- a/db/user.go +++ b/db/user.go @@ -6,21 +6,22 @@ import ( logger "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" ae "joshsoftware/go-e-commerce/apperrors" + "time" ) //User Struct for declaring attributes of User type User struct { - ID int `db:"id" json:"id"` - FirstName string `db:"first_name" json:"first_name"` - LastName string `db:"last_name" json:"last_name"` - Email string `db:"email" json:"email"` - Mobile string `db:"mobile" json:"mobile"` - Address string `db:"address" json:"address"` - Password string `db:"password" json:"password"` - Country string `db:"country" json:"country"` - State string `db:"state" json:"state"` - City string `db:"city" json:"city"` - CreatedAt string `db:"created_at" json:"created_at"` + ID int `db:"id" json:"id"` + FirstName string `db:"first_name" json:"first_name"` + LastName string `db:"last_name" json:"last_name"` + Email string `db:"email" json:"email"` + Mobile string `db:"mobile" json:"mobile"` + Address string `db:"address" json:"address"` + Password string `db:"password" json:"password"` + Country string `db:"country" json:"country"` + State string `db:"state" json:"state"` + City string `db:"city" json:"city"` + CreatedAt time.Time `db:"created_at" json:"created_at"` } //ListUsers function to fetch all Users From Database From d28894bab3a4d4bedf84dbd889d7237ce9727e88 Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Fri, 11 Sep 2020 17:16:23 +0530 Subject: [PATCH 50/51] Added Necessary Error Handling --- service/router.go | 10 ++++++++++ service/session_http.go | 9 +-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/service/router.go b/service/router.go index 47e9aac..20e1cc9 100644 --- a/service/router.go +++ b/service/router.go @@ -41,6 +41,16 @@ func jwtMiddleWare(endpoint http.Handler, deps Dependencies) http.Handler { authToken := req.Header.Get("Token") + //Checking if token not present in header + if len(authToken) < 1 { + responses(rw, http.StatusUnauthorized, errorResponse{ + Error: messageObject{ + Message: "Missing Authorization Token", + }, + }) + return + } + _, _, err := getDataFromToken(authToken) if err != nil { responses(rw, http.StatusUnauthorized, errorResponse{ diff --git a/service/session_http.go b/service/session_http.go index 86c7ca3..24c4e24 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -56,7 +56,6 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { Message: "JSON Decoding Failed", }, }) - return } @@ -145,15 +144,9 @@ func userLogoutHandler(deps Dependencies) http.Handler { func getDataFromToken(Token string) (userID float64, expirationTime int64, err error) { mySigningKey := config.JWTKey() - //Checking if token not present in header - if len(Token) < 1 { - ae.Error(ae.ErrMissingAuthHeader, "Missing Authentication Token From Header", err) - return - } - token, err := jwt.Parse(Token, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("There was an error") + return nil, fmt.Errorf("There was an error while parsing the token") } return mySigningKey, nil }) From 2e3fa5a68244397dd1b957259b454a375a06a0d4 Mon Sep 17 00:00:00 2001 From: Tejas Zambre <[tzambre681@gmail.com]> Date: Mon, 14 Sep 2020 11:34:43 +0530 Subject: [PATCH 51/51] Changed Identifier err1 To err --- service/session_http.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/service/session_http.go b/service/session_http.go index 24c4e24..be30809 100644 --- a/service/session_http.go +++ b/service/session_http.go @@ -62,9 +62,9 @@ func userLoginHandler(deps Dependencies) http.HandlerFunc { //TODO change no need to return user object from Authentication //checking if the user is authenticated or not // by passing the credentials to the AuthenticateUser function - user, err1 := deps.Store.AuthenticateUser(req.Context(), user) - if err1 != nil { - logger.WithField("err", err1.Error()).Error("Invalid Credentials") + user, err = deps.Store.AuthenticateUser(req.Context(), user) + if err != nil { + logger.WithField("err", err.Error()).Error("Invalid Credentials") responses(rw, http.StatusUnauthorized, errorResponse{ Error: messageObject{ Message: "Invalid Credentials",