From f70caf557c683e737f3d58c75af9d620a893a28f Mon Sep 17 00:00:00 2001 From: Nicolas Dinquel Date: Fri, 30 Aug 2019 15:50:59 +0200 Subject: [PATCH] try 1 --- .env | 7 +++ app/auth.go | 79 +++++++++++++++++++++++++++++++ app/errors.go | 15 ++++++ controllers/authController.go | 0 controllers/contactsController.go | 0 main.go | 27 +++++++++++ models/base.go | 41 ++++++++++++++++ utils/utils.go | 15 ++++++ 8 files changed, 184 insertions(+) create mode 100644 .env create mode 100644 app/auth.go create mode 100644 app/errors.go create mode 100644 controllers/authController.go create mode 100644 controllers/contactsController.go create mode 100644 main.go create mode 100644 models/base.go create mode 100644 utils/utils.go diff --git a/.env b/.env new file mode 100644 index 0000000..7ff6aa4 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +db_name = gocontacts +db_pass = password +db_user = postgres +db_type = postgres +db_host = localhost +db_port = 5434 +token_password = thisIsTheJwtSecretPassword //Do not commit to git! \ No newline at end of file diff --git a/app/auth.go b/app/auth.go new file mode 100644 index 0000000..6572542 --- /dev/null +++ b/app/auth.go @@ -0,0 +1,79 @@ +package app + +import ( + "net/http" + u "../utils" + "strings" + "../models" + jwt "github.com/dgrijalva/jwt-go" + "os" + "context" + "fmt" +) + +var JwtAuthentication = func(next http.Handler) http.Handler { + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + notAuth := []string{"/api/user/new", "/api/user/login"} //List of endpoints that doesn't require auth + requestPath := r.URL.Path //current request path + + //check if request does not need authentication, serve the request if it doesn't need it + for _, value := range notAuth { + + if value == requestPath { + next.ServeHTTP(w, r) + return + } + } + + response := make(map[string] interface{}) + tokenHeader := r.Header.Get("Authorization") //Grab the token from the header + + if tokenHeader == "" { //Token is missing, returns with error code 403 Unauthorized + response = u.Message(false, "Missing auth token") + w.WriteHeader(http.StatusForbidden) + w.Header().Add("Content-Type", "application/json") + u.Respond(w, response) + return + } + + splitted := strings.Split(tokenHeader, " ") //The token normally comes in format `Bearer {token-body}`, we check if the retrieved token matched this requirement + if len(splitted) != 2 { + response = u.Message(false, "Invalid/Malformed auth token") + w.WriteHeader(http.StatusForbidden) + w.Header().Add("Content-Type", "application/json") + u.Respond(w, response) + return + } + + tokenPart := splitted[1] //Grab the token part, what we are truly interested in + tk := &models.Token{} + + token, err := jwt.ParseWithClaims(tokenPart, tk, func(token *jwt.Token) (interface{}, error) { + return []byte(os.Getenv("token_password")), nil + }) + + if err != nil { //Malformed token, returns with http code 403 as usual + response = u.Message(false, "Malformed authentication token") + w.WriteHeader(http.StatusForbidden) + w.Header().Add("Content-Type", "application/json") + u.Respond(w, response) + return + } + + if !token.Valid { //Token is invalid, maybe not signed on this server + response = u.Message(false, "Token is not valid.") + w.WriteHeader(http.StatusForbidden) + w.Header().Add("Content-Type", "application/json") + u.Respond(w, response) + return + } + + //Everything went well, proceed with the request and set the caller to the user retrieved from the parsed token + fmt.Sprintf("User %", tk.Username) //Useful for monitoring + ctx := context.WithValue(r.Context(), "user", tk.UserId) + r = r.WithContext(ctx) + next.ServeHTTP(w, r) //proceed in the middleware chain! + }); +} diff --git a/app/errors.go b/app/errors.go new file mode 100644 index 0000000..212ca7b --- /dev/null +++ b/app/errors.go @@ -0,0 +1,15 @@ +package app + +import ( + "net/http" + u "../utils" +) + +var NotFoundHandler = func(next http.Handler) http.Handler { + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + u.Respond(w, u.Message(false, "This resources was not found on our server")) + next.ServeHTTP(w, r) + }) +} \ No newline at end of file diff --git a/controllers/authController.go b/controllers/authController.go new file mode 100644 index 0000000..e69de29 diff --git a/controllers/contactsController.go b/controllers/contactsController.go new file mode 100644 index 0000000..e69de29 diff --git a/main.go b/main.go new file mode 100644 index 0000000..6329ea5 --- /dev/null +++ b/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/gorilla/mux" + "./app" + "os" + "fmt" + "net/http" +) + +func main() { + + router := mux.NewRouter() + router.Use(app.JwtAuthentication) //attach JWT auth middleware + + port := os.Getenv("PORT") //Get port from .env file, we did not specify any port so this should return an empty string when tested locally + if port == "" { + port = "8000" //localhost + } + + fmt.Println(port) + + err := http.ListenAndServe(":" + port, router) //Launch the app, visit localhost:8000/api + if err != nil { + fmt.Print(err) + } +} diff --git a/models/base.go b/models/base.go new file mode 100644 index 0000000..d2a2710 --- /dev/null +++ b/models/base.go @@ -0,0 +1,41 @@ +package models + +import ( + _ "github.com/jinzhu/gorm/dialects/postgres" + "github.com/jinzhu/gorm" + "os" + "github.com/joho/godotenv" + "fmt" +) + +var db *gorm.DB //database + +func init() { + + e := godotenv.Load() //Load .env file + if e != nil { + fmt.Print(e) + } + + username := os.Getenv("db_user") + password := os.Getenv("db_pass") + dbName := os.Getenv("db_name") + dbHost := os.Getenv("db_host") + + + dbUri := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=disable password=%s", dbHost, username, dbName, password) //Build connection string + fmt.Println(dbUri) + + conn, err := gorm.Open("postgres", dbUri) + if err != nil { + fmt.Print(err) + } + + db = conn + db.Debug().AutoMigrate(&Account{}, &Contact{}) //Database migration +} + +//returns a handle to the DB object +func GetDB() *gorm.DB { + return db +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..6a90e23 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,15 @@ +package utils + +import ( + "encoding/json" + "net/http" +) + +func Message(status bool, message string) (map[string]interface{}) { + return map[string]interface{} {"status" : status, "message" : message} +} + +func Respond(w http.ResponseWriter, data map[string] interface{}) { + w.Header().Add("Content-Type", "application/json") + json.NewEncoder(w).Encode(data) +}