Skip to content

Commit

Permalink
Merge pull request #28 from titouanfreville/issue-26-login
Browse files Browse the repository at this point in the history
Issue 26 login
  • Loading branch information
titouanfreville authored Mar 7, 2017
2 parents 18568f5 + 11a9634 commit ed59146
Show file tree
Hide file tree
Showing 18 changed files with 1,873 additions and 1,401 deletions.
873 changes: 506 additions & 367 deletions README.md

Large diffs are not rendered by default.

119 changes: 105 additions & 14 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
package api

import (
"bytes"
"crypto/rand"
"encoding/base32"
"flag"
"log"
"net/http"

jwt "github.com/dgrijalva/jwt-go"
"github.com/goware/jwtauth"
"github.com/jinzhu/gorm"
"github.com/pressly/chi"
"github.com/pressly/chi/docgen"
"github.com/pressly/chi/middleware"
chiRender "github.com/pressly/chi/render"
"github.com/titouanfreville/popcubeapi/configs"
"github.com/titouanfreville/popcubeapi/datastores"
"github.com/titouanfreville/popcubeapi/models"
"github.com/titouanfreville/popcubeapi/utils"
renderPackage "github.com/unrolled/render"
)

type saveDb struct {
Expand All @@ -22,12 +30,50 @@ type saveDb struct {
type key string

var (
routes = flag.Bool("routes", false, "Generate router documentation")
dbStore = saveDb{}
error422 = utils.NewAPIError(422, "parse.request.body", "Request json object not correct.")
error503 = utils.NewAPIError(503, "database.maintenance", "Database is currently in maintenance state. We are doing our best to get it back online ASAP.")
hmacSampleSecret []byte
tokenAuth *jwtauth.JwtAuth
userToken *jwt.Token
encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769")
render = renderPackage.New()
routes = flag.Bool("routes", false, "Generate router documentation")
dbStore = saveDb{}
error401 = utils.NewAPIError(401, "unauthorized", "You did not login into the app. Please login to access those resources")
error422 = utils.NewAPIError(422, "parse.request.body", "Request json object not correct.")
error503 = utils.NewAPIError(503, "database.maintenance", "Database is currently in maintenance state. We are doing our best to get it back online ASAP.")
)

func newRandomString(length int) string {
var b bytes.Buffer
str := make([]byte, length+8)
rand.Read(str)
encoder := base32.NewEncoder(encoding, &b)
encoder.Write(str)
encoder.Close()
b.Truncate(length)
return b.String()
}
func initAuth() {
secret := newRandomString(100)
hmacSampleSecret = []byte(secret)
tokenAuth = jwtauth.New("HS256", hmacSampleSecret, hmacSampleSecret)
}

// createToken create JWT auth token for current login user
func createToken(user models.User) (string, error) {
claims := jwt.MapClaims{
"name": user.Username,
"email": user.Email,
}
unsignedToken := *jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := unsignedToken.SignedString(hmacSampleSecret)

if err != nil {
return "", err
}

return tokenString, nil
}

// newRouter initialise api serveur.
func newRouter() *chi.Mux {
return chi.NewRouter()
Expand All @@ -41,7 +87,7 @@ func initMiddleware(router *chi.Mux) {
router.Use(middleware.Recoverer)
router.Use(middleware.StripSlashes)
router.Use(middleware.Timeout(5 * 1000))
// router.Use(middleware.Heartbeat("/heartbeat"))
router.Use(middleware.Heartbeat("/heartbeat"))
router.Use(middleware.CloseNotify)
}

Expand Down Expand Up @@ -69,6 +115,7 @@ func basicRoutes(router *chi.Mux) {
router.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
})
router.Get("/heartbeat", func(w http.ResponseWriter, r *http.Request) {})
// swagger:route GET /panic Test panic
//
// Should result in 500
Expand All @@ -81,6 +128,52 @@ func basicRoutes(router *chi.Mux) {
router.Get("/panic", func(w http.ResponseWriter, r *http.Request) {
panic("C'est la panique, panique, panique. Sur le périphérique")
})
// swagger:route POST /login Login login
//
// Try to log user in
//
// Login user with provided USERNAME && Password
//
// Responses:
// 200: loginOk
// 404: incorrectIds
// 422: wrongEntity
// 503: databaseError
router.Post("/login", loginMiddleware)
}

// loginMiddleware login funcion providing user && jwt auth token
func loginMiddleware(w http.ResponseWriter, r *http.Request) {
var data struct {
Login string `json:"login"`
Password string `json:"password"`
OmitID interface{} `json:"id,omitempty"`
}
store := datastores.Store()
response := loginOk{}
db := dbStore.db
request := r.Body
err := chiRender.Bind(request, &data)
if err != nil {
render.JSON(w, error422.StatusCode, error422)
return
}
if err := db.DB().Ping(); err == nil {
user, err := store.User().Login(data.Login, data.Password, db)
if err == nil {
var terr error
response.User = user
response.Token, terr = createToken(user)
if terr == nil {
render.JSON(w, 200, response)
return
}
render.JSON(w, err.StatusCode, err)
return
}
}
render.JSON(w, error503.StatusCode, error503)

}

// StartAPI initialise the api with provided host and port.
Expand All @@ -93,7 +186,7 @@ func StartAPI(hostname string, port string, DbConnectionInfo *configs.DbConnecti
host := DbConnectionInfo.Host
dbport := DbConnectionInfo.Port
dbStore.db = datastores.Store().InitConnection(user, db, pass, host, dbport)

initAuth()
initMiddleware(router)
basicRoutes(router)
initAvatarRoute(router)
Expand All @@ -109,14 +202,12 @@ func StartAPI(hostname string, port string, DbConnectionInfo *configs.DbConnecti
// Passing -routes to the program will generate docs for the above
// router definition. See the `routes.json` file in this folder for
// the output.
if *routes {
log.Println(docgen.JSONRoutesDoc(router))
log.Println(docgen.BuildDoc(router))
log.Println(docgen.MarkdownRoutesDoc(router, docgen.MarkdownOpts{
ProjectPath: "github.com/titouanfreville/popcubeapi",
Intro: "Welcomme to popcube user api.",
}))
}
log.Println(docgen.JSONRoutesDoc(router))
log.Println(docgen.BuildDoc(router))
log.Println(docgen.MarkdownRoutesDoc(router, docgen.MarkdownOpts{
ProjectPath: "github.com/titouanfreville/popcubeapi",
Intro: "Welcomme to popcube user api.",
}))

http.ListenAndServe(hostname+":"+port, router)
}
20 changes: 20 additions & 0 deletions api/api_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,23 @@ type userObjectParam struct {
}

// <><><><><> <><><><><> <><><><><> <><><><><> //
// <><><><><> LOGIN PARAMETERS <><><><><> //

// loginParameterModel is the object you have to pass to login
//
// swagger:model loginParameterModel
type loginParameterModel struct {
// user name
// required: true
Login string `json:"login"`
// user password hashed
// required: true
Password string `json:"password"`
}

// swagger:parameters Login
type loginParam struct {
// Login informations
// in:body
Login loginParameterModel `json:"login"`
}
18 changes: 18 additions & 0 deletions api/api_responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ type generalOk struct {
Message string `json:"message,omitempty"`
}

// loginOk when login correctly proceed, return the user and an auth token.
//
// swagger:response loginOk
type loginOk struct {
// in:body
User models.User
// in:body
Token string
}

// ---------------------------------------------------
// Errors --------------------------------------------

Expand Down Expand Up @@ -43,6 +53,14 @@ type databaseError struct {
Error utils.AppError
}

// incorrectIds return error login message
//
// swagger:response incorrectIds
type incorrectIds struct {
// in:body
Error utils.AppError
}

// ---------------------------------------------------
// Unknow --------------------------------------------

Expand Down
15 changes: 8 additions & 7 deletions api/avatar_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
"net/http"
"strconv"

"github.com/goware/jwtauth"
"github.com/pressly/chi"
chiRender "github.com/pressly/chi/render"
"github.com/titouanfreville/popcubeapi/datastores"
"github.com/titouanfreville/popcubeapi/models"
renderPackage "github.com/unrolled/render"
)

const (
Expand All @@ -20,6 +20,8 @@ const (

func initAvatarRoute(router chi.Router) {
router.Route("/avatar", func(r chi.Router) {
r.Use(tokenAuth.Verifier)
r.Use(jwtauth.Authenticator)
// swagger:route GET /avatar Avatars getAllAvatar
//
// Get avatars
Expand Down Expand Up @@ -146,7 +148,6 @@ func avatarContext(next http.Handler) http.Handler {

func getAllAvatar(w http.ResponseWriter, r *http.Request) {
store := datastores.Store()
render := renderPackage.New()
db := dbStore.db
if err := db.DB().Ping(); err == nil {
result := store.Avatar().GetAll(db)
Expand All @@ -158,7 +159,7 @@ func getAllAvatar(w http.ResponseWriter, r *http.Request) {

func getAvatarFromName(w http.ResponseWriter, r *http.Request) {
store := datastores.Store()
render := renderPackage.New()

db := dbStore.db
if err := db.DB().Ping(); err == nil {
name := r.Context().Value(avatarNameKey).(string)
Expand All @@ -171,7 +172,7 @@ func getAvatarFromName(w http.ResponseWriter, r *http.Request) {

func getAvatarFromLink(w http.ResponseWriter, r *http.Request) {
store := datastores.Store()
render := renderPackage.New()

db := dbStore.db
if err := db.DB().Ping(); err == nil {
link := r.Context().Value(avatarLinkKey).(string)
Expand All @@ -188,7 +189,7 @@ func newAvatar(w http.ResponseWriter, r *http.Request) {
OmitID interface{} `json:"id,omitempty"`
}
store := datastores.Store()
render := renderPackage.New()

db := dbStore.db
request := r.Body
err := chiRender.Bind(request, &data)
Expand All @@ -214,7 +215,7 @@ func updateAvatar(w http.ResponseWriter, r *http.Request) {
OmitID interface{} `json:"id,omitempty"`
}
store := datastores.Store()
render := renderPackage.New()

db := dbStore.db
request := r.Body
err := chiRender.Bind(request, &data)
Expand All @@ -238,7 +239,7 @@ func updateAvatar(w http.ResponseWriter, r *http.Request) {
func deleteAvatar(w http.ResponseWriter, r *http.Request) {
avatar := r.Context().Value(oldAvatarKey).(models.Avatar)
store := datastores.Store()
render := renderPackage.New()

message := deleteMessageModel{
Object: avatar,
}
Expand Down
Loading

0 comments on commit ed59146

Please sign in to comment.