Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cde43e4
Initial commit adding Todo models functionality
ShahriyarR May 3, 2020
3d9ce8e
Adding todos controllers
ShahriyarR May 3, 2020
b9e7705
Adding todo middleware
ShahriyarR May 4, 2020
8880006
Added TODO for changing middleware logic
ShahriyarR May 5, 2020
79ec0d4
Committing non-working code
ShahriyarR May 6, 2020
b5c2a9e
Thanks God now general validation actions are working - Able to add a…
ShahriyarR May 6, 2020
e951aea
Finalized Todo api integration with User api
ShahriyarR May 7, 2020
9886ae0
Started to implement JWT
ShahriyarR May 11, 2020
16d72e6
Initial code for /login action
ShahriyarR May 11, 2020
8130a5d
Again non-working another fucking code portion
ShahriyarR May 12, 2020
4c854e4
Implemented the Login
ShahriyarR May 12, 2020
151cd3d
Finalized needed features
ShahriyarR May 13, 2020
6d8987c
Code refactoring making seperate function for id token extraction
ShahriyarR May 17, 2020
987eaa1
Added new test config file and new function
ShahriyarR May 17, 2020
92ef88f
Started to implement first Unit test
ShahriyarR May 17, 2020
7824966
Added failed test
ShahriyarR May 18, 2020
1b08381
First working test is ready
ShahriyarR May 18, 2020
66f9d3c
Added further tests to test User model at db level
ShahriyarR May 25, 2020
859e412
fixed the length
ShahriyarR May 25, 2020
85b5057
Added EmptyPassword and EmptyPasswordHash tests
ShahriyarR May 26, 2020
7ef977b
Fixing import errors and added empty field tests
ShahriyarR May 27, 2020
6fa213e
model tests continued
ShahriyarR May 27, 2020
0d4a312
Added test for DeleteUser
ShahriyarR May 30, 2020
33d0811
Added several tests for testing Todo model
ShahriyarR Jun 1, 2020
25782a7
finalized todo models tests
ShahriyarR Jun 1, 2020
0f36495
Started to write controller tests
ShahriyarR Jun 2, 2020
022a9fb
Added first login test
ShahriyarR Jun 3, 2020
953889f
TestLoginWithValidationPassWrongJSONFormat completed
ShahriyarR Jun 4, 2020
fd8fa3c
fixing some unhandled exceptions
ShahriyarR Jun 4, 2020
e78772b
Adding few changes
ShahriyarR Jun 4, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Episode_*/
.idea
1 change: 1 addition & 0 deletions Project1_Simple_REST_API/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
100 changes: 100 additions & 0 deletions Project1_Simple_REST_API/Golang_Gorilla/golang_rest_api/auth/token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package auth

import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"time"

jwt "github.com/dgrijalva/jwt-go"
)

var AccessSecret = []byte("my-super-secret-access-secret")
var RefreshSecret = []byte("my-super-secret-refresh-secret")

func CreateAccessToken(userId uint) (string, error) {
claims := jwt.MapClaims{}
claims["authorized"] = true
claims["user_id"] = userId
claims["exp"] = time.Now().Add(time.Minute * 3).Unix()
claims["iat"] = time.Now().Unix()
claims["type"] = "AccessToken"
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(AccessSecret)
}

func CreateRefreshToken(userId uint) (string, error) {
claims := jwt.MapClaims{}
claims["authorized"] = true
claims["user_id"] = userId
claims["exp"] = time.Now().Add(time.Hour * 3).Unix()
claims["iat"] = time.Now().Unix()
claims["type"] = "RefreshToken"
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(RefreshSecret)
}

func TokenValid(r *http.Request) (*jwt.Token, error) {
tokenString := ExtractToken(r)
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return AccessSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
Pretty(claims)
}
return token, nil
}

func ExtractToken(r *http.Request) string {
keys := r.URL.Query()
token := keys.Get("token")
if token != "" {
return token
}
bearerToken := r.Header.Get("Authorization")
if len(strings.Split(bearerToken, " ")) == 2 {
return strings.Split(bearerToken, " ")[1]
}
return ""
}

func ExtractTokenID(r *http.Request) (uint, error) {
tokenString := ExtractToken(r)
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return AccessSecret, nil
})
if err != nil {
return 0, err
}
claims, ok := token.Claims.(jwt.MapClaims)
if ok && token.Valid {
uid, err := strconv.ParseUint(fmt.Sprintf("%.0f", claims["user_id"]), 10, 64)
if err != nil {
return 0, err
}
return uint(uid), nil
}
return 0, nil
}

//Pretty display the claims nicely in the terminal
func Pretty(data interface{}) {
b, err := json.MarshalIndent(data, "", " ")
if err != nil {
log.Println(err)
return
}
fmt.Println(string(b))
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package controllers

import (
"golang_restful_api/models"
"golang_restful_api/utils"
"golang_rest_api/models"
"golang_rest_api/utils"
"net/http"
)

Expand All @@ -28,6 +28,32 @@ func (a *Users) Delete(w http.ResponseWriter, r *http.Request) {
utils.Respond(w, &GenericError{Message: err.Error()})
return
}

w.WriteHeader(http.StatusNoContent)
}

// Delete handles DELETE requests and removes items from the database
func (t *Todos) Delete(w http.ResponseWriter, r *http.Request) {
acc, err := t.getTokenAndUser(w, r)

tid := getTodoID(r)
t.l.Println("[DEBUG] deleting record id", tid)

err = t.ts.DeleteTodo(acc, tid)
if err == models.ErrNotFound {
t.l.Println("[ERROR] deleting record id does not exist")

w.WriteHeader(http.StatusNotFound)
utils.Respond(w, &GenericError{Message: err.Error()})
return
}

if err != nil {
t.l.Println("[ERROR] deleting record", err)

w.WriteHeader(http.StatusInternalServerError)
utils.Respond(w, &GenericError{Message: err.Error()})
return
}

w.WriteHeader(http.StatusNoContent)
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package controllers

import (
"golang_restful_api/models"
"golang_restful_api/utils"
"golang_rest_api/models"
"golang_rest_api/utils"
"net/http"
)

// ListAll handles GET requests and returns all current users
func (a *Users) ListAll(w http.ResponseWriter, r *http.Request) {
a.l.Println("[DEBUG] get all records")

accs, err := a.us.GetUsers()
switch err {
case nil:
Expand Down Expand Up @@ -67,3 +66,50 @@ func (a *Users) ListSingle(w http.ResponseWriter, r *http.Request) {
a.l.Println("[ERROR] serializing user", err)
}
}

// ListAll handles GET requests and returns all current todos for a given user
func (t *Todos) ListAll(w http.ResponseWriter, r *http.Request) {
t.l.Println("[DEBUG] get all records")
acc, err := t.getTokenAndUser(w, r)
todos, err := t.ts.GetTodos(acc)
if err != nil {
// TODO: do better error checking
t.l.Println("[ERROR] fetching todos", err)
w.WriteHeader(http.StatusInternalServerError)
utils.Respond(w, &GenericError{Message: err.Error()})
}

err = utils.Respond(w, todos)
if err != nil {
// we should never be here but log the error just incase
t.l.Println("[ERROR] serializing todos", err)
}
}

// ListSingle handles GET requests
func (t *Todos) ListSingle(w http.ResponseWriter, r *http.Request) {
acc, err := t.getTokenAndUser(w, r)
tid := getTodoID(r)
todo, err := t.ts.GetTodoByID(acc, tid)
switch err {
case nil:

case models.ErrNotFound:
t.l.Println("[ERROR] fetching todo", err)

w.WriteHeader(http.StatusNotFound)
utils.Respond(w, &GenericError{Message: err.Error()})
return
default:
t.l.Println("[ERROR] fetching todo", err)

w.WriteHeader(http.StatusInternalServerError)
utils.Respond(w, &GenericError{Message: err.Error()})
return
}
err = utils.Respond(w, todo)
if err != nil {
// we should never be here but log the error just incase
t.l.Println("[ERROR] serializing todos", err)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package controllers

import (
"golang_rest_api/auth"
"golang_rest_api/models"
"golang_rest_api/utils"
"net/http"
)

type KeyLogin struct{}

func (us *Users) Login(w http.ResponseWriter, r *http.Request) {
login := r.Context().Value(KeyLogin{}).(*models.Login)
foundUser, err := us.us.Authenticate(login.Email, login.Password)
if err != nil {
us.l.Println("[ERROR] Something went wrong with user authentication", err)
w.WriteHeader(http.StatusBadRequest)
utils.Respond(w, &GenericError{Message: "Something went wrong with user authentication"})
return
}
accessToken, err := auth.CreateAccessToken(foundUser.ID)
if err != nil {
us.l.Println("[ERROR] Something went wrong with user Access token creation", err)
w.WriteHeader(http.StatusUnprocessableEntity)
utils.Respond(w, &GenericError{Message: "Something went wrong with user Access token creation"})
return
}

refreshToken, err := auth.CreateRefreshToken(foundUser.ID)
if err != nil {
us.l.Println("[ERROR] Something went wrong with user Refresh token creation", err)
w.WriteHeader(http.StatusUnprocessableEntity)
utils.Respond(w, &GenericError{Message: "Something went wrong with user Refresh token creation"})
return
}
tokens := map[string]string {
"AccessToken": accessToken,
"RefreshToken": refreshToken,
}
err = utils.Respond(w, tokens)
if err != nil {
us.l.Println("[ERROR] serializing tokens", err)
}
}
Loading