From 308ff988300a563fe7c8a5e9303d079191b956d7 Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Mon, 19 Dec 2016 23:23:52 +0100 Subject: [PATCH] Finish cleaning dependencies --- cmd/budgeteer/main.go | 12 ++++---- http/http.go | 55 +++++++++++++++++++++++++--------- jwt/login.go | 70 ++++++++++++++++++++++++------------------- token.go | 9 +++++- 4 files changed, 95 insertions(+), 51 deletions(-) diff --git a/cmd/budgeteer/main.go b/cmd/budgeteer/main.go index 4d7cb93..8c97144 100644 --- a/cmd/budgeteer/main.go +++ b/cmd/budgeteer/main.go @@ -1,16 +1,18 @@ package main import ( - "net/http" - - "github.com/gin-gonic/gin" "git.javil.eu/jacob1123/budgeteer/http" + "git.javil.eu/jacob1123/budgeteer/jwt" "git.javil.eu/jacob1123/budgeteer/postgres" ) func main() { us := &postgres.UserService{} + tv := &jwt.TokenVerifier{} - h := &http.Handler{UserService=us} + h := &http.Handler{ + UserService: us, + TokenVerifier: tv, + } h.Serve() -} \ No newline at end of file +} diff --git a/http/http.go b/http/http.go index c6c77cf..6c3792e 100644 --- a/http/http.go +++ b/http/http.go @@ -2,6 +2,7 @@ package http import ( "net/http" + "time" "git.javil.eu/jacob1123/budgeteer" @@ -10,25 +11,30 @@ import ( // Handler handles incoming requests type Handler struct { - UserService budgeteer.UserService + UserService budgeteer.UserService + TokenVerifier budgeteer.TokenVerifier } -func (h *Handler) Serve() { +const ( + expiration = 72 + authCookie = "authentication" +) +func (h *Handler) Serve() { router := gin.Default() router.LoadHTMLGlob("./templates/*") router.Static("/static", "./static") router.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "index", nil) }) - router.GET("/login", login) + router.GET("/login", h.login) api := router.Group("/api/v1") { api.GET("/logout", logout) api.GET("/login", func(c *gin.Context) { c.Redirect(http.StatusPermanentRedirect, "/login") }) - api.POST("/login", loginPost) + api.POST("/login", h.loginPost) // Unauthenticated routes api.GET("/check", func(c *gin.Context) { @@ -42,34 +48,41 @@ func (h *Handler) Serve() { r := api.Group("/restricted") { //r.Use(middleware.JWT([]byte(secret))) - r.GET("", restricted) + r.GET("", h.restricted) } } router.Run(":1323") } -func restricted(c *gin.Context) { - claims, ok := verifyLogin(c) - if !ok { +func (h *Handler) restricted(c *gin.Context) { + token, err := h.verifyLogin(c) + if err != nil { c.Redirect(http.StatusTemporaryRedirect, "/login") return } - name := claims["name"].(string) + name := token.GetName() c.String(http.StatusOK, "Welcome "+name+"!") } -func verifyLogin(c *gin.Context) error { +func (h *Handler) verifyLogin(c *gin.Context) (budgeteer.Token, error) { tokenString, err := c.Cookie(authCookie) if err != nil { return nil, err } + token, err := h.TokenVerifier.VerifyToken(tokenString) + if err != nil { + c.SetCookie(authCookie, "", -1, "", "", false, false) + return nil, err + } + + return token, nil } -func login(c *gin.Context) { - if _, ok := verifyLogin(c); ok { +func (h *Handler) login(c *gin.Context) { + if _, err := h.verifyLogin(c); err == nil { c.Redirect(http.StatusTemporaryRedirect, "/api/v1/hello") return } @@ -81,7 +94,11 @@ func logout(c *gin.Context) { clearLogin(c) } -func loginPost(c *gin.Context) { +func clearLogin(c *gin.Context) { + c.SetCookie(authCookie, "", -1, "", "", false, true) +} + +func (h *Handler) loginPost(c *gin.Context) { username, _ := c.GetPostForm("username") password, _ := c.GetPostForm("password") @@ -90,5 +107,15 @@ func loginPost(c *gin.Context) { return } - loginSuccess(c, username, "Jan Bader") + t, err := h.TokenVerifier.CreateToken(username, "Jan Bader") + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + } + + maxAge := (int)((expiration * time.Hour).Seconds()) + c.SetCookie(authCookie, t, maxAge, "", "", false, true) + + c.JSON(http.StatusOK, map[string]string{ + "token": t, + }) } diff --git a/jwt/login.go b/jwt/login.go index 34cc497..39264ec 100644 --- a/jwt/login.go +++ b/jwt/login.go @@ -2,25 +2,44 @@ package jwt import ( "fmt" - "net/http" "time" + "git.javil.eu/jacob1123/budgeteer" "github.com/dgrijalva/jwt-go" - "gopkg.in/gin-gonic/gin.v1" ) +// TokenVerifier verifies Tokens +type TokenVerifier struct { +} +type Token struct { + username string + name string + expiry float64 +} + const ( expiration = 72 secret = "uditapbzuditagscwxuqdflgzpbu´ßiaefnlmzeßtrubiadern" - authCookie = "authentication" ) -func verifyLogin(c *gin.Context) (jwt.MapClaims, error) { - tokenString, err := c.Cookie(authCookie) +// CreateToken creates a new token from username and name +func (tv *TokenVerifier) CreateToken(username string, name string) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "usr": username, + "name": name, + "exp": time.Now().Add(time.Hour * expiration).Unix(), + }) + + // Generate encoded token and send it as response. + t, err := token.SignedString([]byte(secret)) if err != nil { - return nil, err + return "", err } + return t, nil +} + +func (tv *TokenVerifier) VerifyToken(tokenString string) (budgeteer.Token, error) { 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"]) @@ -28,17 +47,20 @@ func verifyLogin(c *gin.Context) (jwt.MapClaims, error) { return []byte(secret), nil }) if err != nil { - c.SetCookie(authCookie, "", -1, "", "", false, false) return nil, err } claims, err := verifyToken(token) if err != nil { - c.SetCookie(authCookie, "", -1, "", "", false, false) return nil, err } - return claims, nil + tkn := &Token{ + username: claims["usr"].(string), + name: claims["name"].(string), + expiry: claims["exp"].(float64), + } + return tkn, nil } func verifyToken(token *jwt.Token) (jwt.MapClaims, error) { @@ -58,28 +80,14 @@ func verifyToken(token *jwt.Token) (jwt.MapClaims, error) { return claims, nil } -func loginSuccess(c *gin.Context, username string, name string) { - // Create token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "usr": username, - "name": name, - "exp": time.Now().Add(time.Hour * expiration).Unix(), - }) - - // Generate encoded token and send it as response. - t, err := token.SignedString([]byte(secret)) - if err != nil { - c.AbortWithStatus(http.StatusUnauthorized) - } - - maxAge := (int)((expiration * time.Hour).Seconds()) - c.SetCookie(authCookie, t, maxAge, "", "", false, true) - - c.JSON(http.StatusOK, map[string]string{ - "token": t, - }) +func (t *Token) GetName() string { + return t.name } -func clearLogin(c *gin.Context) { - c.SetCookie(authCookie, "", -1, "", "", false, true) +func (t *Token) GetUsername() string { + return t.username +} + +func (t *Token) GetExpiry() float64 { + return t.expiry } diff --git a/token.go b/token.go index 9700216..6637202 100644 --- a/token.go +++ b/token.go @@ -3,5 +3,12 @@ package budgeteer // Token contains data that authenticates a user type Token interface { GetUsername() string - GetExpiry() int + GetName() string + GetExpiry() float64 +} + +// TokenVerifier verifies a Token +type TokenVerifier interface { + VerifyToken(string) (Token, error) + CreateToken(string, string) (string, error) }