diff --git a/Readme.md b/Readme.md index fd334ed..b837502 100644 --- a/Readme.md +++ b/Readme.md @@ -14,3 +14,4 @@ To regenerate docs simply run `bee generate docs` - company controller, create databases and so on - Update not found to json +- load db connections from config diff --git a/controllers/auth.go b/controllers/auth.go index befb97e..11c68d8 100644 --- a/controllers/auth.go +++ b/controllers/auth.go @@ -1,7 +1,7 @@ package controllers import ( - auth "multitenantStack/services/authentication" + auth "multitenantStack/services" "time" jwt "github.com/dgrijalva/jwt-go" @@ -37,7 +37,7 @@ func (c *AuthController) Login() { } if c.Ctx.Input.Method() != "POST" { - c.ServeJsonError("Method not allowed") + c.ServeJSONError("Method not allowed") return } @@ -46,13 +46,13 @@ func (c *AuthController) Login() { email := c.GetString("email") password := c.GetString("password") - //TODO: check against main database, get company id and veryfy password - companyName := "" - companyUserId := 5 - //TODO: if found query the company database to get roleid, and name + //TODO: check against main database, get company id and verify password + companyName := "company_1" + companyUserID := 5 + //TODO: if found query the company database to get roleID, and name name := "Lukas" - roleId := 5 + roleID := 5 tokenString := "" if email == "admin@admin.at" && password == "my password" { @@ -60,13 +60,13 @@ func (c *AuthController) Login() { tokenString = auth.CreateToken(jwt.MapClaims{ "email": email, "companyName": companyName, - "companyUserId": companyUserId, + "companyUserID": companyUserID, "name": name, - "roleId": roleId, - "expires": time.Now().Unix() + 3600, + "roleID": roleID, + "exp": time.Now().Unix() + 3600, }) } else { - c.ServeJsonError("Invalid user/password") + c.ServeJSONError("Invalid user/password") return } diff --git a/controllers/base.go b/controllers/base.go index ba041a5..417dccd 100644 --- a/controllers/base.go +++ b/controllers/base.go @@ -4,35 +4,44 @@ import ( "github.com/astaxie/beego" ) -type JsonBasicResponse struct { +// JSONBasicResponse The minimal JSON response +type JSONBasicResponse struct { Status int Message string } -const JSON_ERROR int = 400 -const JSON_INT_ERROR int = 500 -const JSON_SUCCESS int = 200 +// JSONError code for a input error +const JSONError int = 400 + +// JSONInternalError code for an internal error +const JSONInternalError int = 500 + +// JSONSuccess code for a success +const JSONSuccess int = 200 // BaseController operations for BaseController type BaseController struct { beego.Controller } -func (c *BaseController) ServeJsonError(message string) { - json := JsonBasicResponse{JSON_ERROR, message} +// ServeJSONError respond with a JSON error +func (c *BaseController) ServeJSONError(message string) { + json := JSONBasicResponse{JSONError, message} c.Data["json"] = &json ///c.Ctx.ResponseWriter.WriteHeader(400) c.ServeJSON() } -func (c *BaseController) ServeJsonErrorWithCode(errorcode int, message string) { - json := JsonBasicResponse{errorcode, message} +// ServeJSONErrorWithCode respond with a JSON error and specify code +func (c *BaseController) ServeJSONErrorWithCode(errorcode int, message string) { + json := JSONBasicResponse{errorcode, message} c.Data["json"] = &json c.ServeJSON() } -func (c *BaseController) ServeJsonSuccess(message string) { - json := JsonBasicResponse{JSON_SUCCESS, message} +// ServeJSONSuccess respond with a JSON success message +func (c *BaseController) ServeJSONSuccess(message string) { + json := JSONBasicResponse{JSONSuccess, message} c.Data["json"] = &json c.ServeJSON() } diff --git a/controllers/baseAPI.go b/controllers/baseAPI.go index 3a9742f..6b5727d 100644 --- a/controllers/baseAPI.go +++ b/controllers/baseAPI.go @@ -1,29 +1,47 @@ package controllers -// BaseController operations for APIs +import ( + "database/sql" + "fmt" + companydb "multitenantStack/services" + + "github.com/astaxie/beego/orm" + jwt "github.com/dgrijalva/jwt-go" +) + +// BaseAPIController operations for APIs type BaseAPIController struct { BaseController } -func (this *BaseAPIController) Prepare() { +var jwtSession jwt.MapClaims +var companyDB *sql.DB +var o orm.Ormer - /* - //Lo que quieras hacer en todos los controladores - // O puede ser leĆ­do de una cabecera HEADER!! - tokenString := this.Ctx.Request.Header.Get("X-JWTtoken") - et := jwtbeego.EasyToken{} - valid, issuer, _ := et.ValidateToken(tokenString) - if !valid { - this.Ctx.Output.SetStatus(401) - this.ServeJsonError("Invalid Token") - } - /* - userSession := this.GetSession("username") +//var database sql.database - if userSession == nil || userSession != issuer { - this.Ctx.Output.SetStatus(401) - this.ServeJsonError("Invalid Session") - } - */ - //return +// Prepare parse all requests that come after this controller for valid auth +func (c *BaseAPIController) Prepare() { + + tokenString := c.Ctx.Request.Header.Get("X-JWTtoken") + + if tokenString == "" { + c.ServeJSONError("No Token provided") + return + } + + token, db, err := companydb.GetDatabase(tokenString) + if err != nil { + c.ServeJSONError("Token invalid") + return + } + + jwtSession = token + companyDB = db + o, err = orm.NewOrmWithDB("postgres", "company", companyDB) + if err != nil { + fmt.Println(err.Error()) + c.ServeJSONError("internal") + return + } } diff --git a/controllers/contact.go b/controllers/contact.go index b6e4a76..eec9533 100644 --- a/controllers/contact.go +++ b/controllers/contact.go @@ -7,12 +7,12 @@ import ( "strconv" "strings" - "github.com/astaxie/beego" + "github.com/astaxie/beego/orm" ) // ContactController operations for Contact type ContactController struct { - beego.Controller + BaseAPIController } // URLMapping ... @@ -56,7 +56,7 @@ func (c *ContactController) Post() { func (c *ContactController) GetOne() { idStr := c.Ctx.Input.Param(":id") id, _ := strconv.Atoi(idStr) - v, err := models.GetContactById(id) + v, err := models.GetContactById(orm.NewOrm(), id) if err != nil { c.Data["json"] = err.Error() } else { @@ -119,12 +119,14 @@ func (c *ContactController) GetAll() { } } - l, err := models.GetAllContact(query, fields, sortby, order, offset, limit) + ob, _ := orm.NewOrmWithDB("postgres", "default", companyDB) + l, err := models.GetAllContact(ob, query, fields, sortby, order, offset, limit) if err != nil { c.Data["json"] = err.Error() } else { c.Data["json"] = l } + c.ServeJSON() } diff --git a/controllers/error.go b/controllers/error.go index a4d4aed..e1dc369 100644 --- a/controllers/error.go +++ b/controllers/error.go @@ -1,13 +1,16 @@ package controllers +// ErrorController Handle all errors type ErrorController struct { BaseController } +// Error404 handle a 404 func (c *ErrorController) Error404() { - c.ServeJsonErrorWithCode(404, "Not Found") + c.ServeJSONErrorWithCode(404, "Not Found") } +// Error500 handle a 500 func (c *ErrorController) Error500() { - c.ServeJsonErrorWithCode(500, "Internal Server Error") + c.ServeJSONErrorWithCode(500, "Internal Server Error") } diff --git a/controllers/index.go b/controllers/index.go index 9f8fa0f..b0acf97 100644 --- a/controllers/index.go +++ b/controllers/index.go @@ -5,10 +5,12 @@ type IndexController struct { BaseController } +// Get Index response for get func (c *IndexController) Get() { - c.ServeJsonSuccess("multitenant API") + c.ServeJSONSuccess("multitenant API") } +// Post Index response for post func (c *IndexController) Post() { - c.ServeJsonSuccess("multitenant API") + c.ServeJSONSuccess("multitenant API") } diff --git a/lastupdate.tmp b/lastupdate.tmp index 68ada61..fef0110 100755 --- a/lastupdate.tmp +++ b/lastupdate.tmp @@ -1 +1 @@ -{"/Users/LB/go/src/multitenantStack/controllers":1541598684943144901} \ No newline at end of file +{"/Users/LB/go/src/multitenantStack/controllers":1541617005449208486} \ No newline at end of file diff --git a/main.go b/main.go index 846828c..10db060 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,8 @@ package main import ( _ "multitenantStack/routers" + auth "multitenantStack/services" + "time" "github.com/astaxie/beego" "github.com/astaxie/beego/orm" @@ -10,6 +12,8 @@ import ( func init() { orm.RegisterDataBase("default", "postgres", "host=127.0.0.1 port=5435 user=postgres password=postgre dbname=company_template sslmode=disable") + auth.InitJWTService() + orm.DefaultTimeLoc = time.UTC if beego.BConfig.RunMode == "dev" { beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" diff --git a/models/contact.go b/models/contact.go index f6645e4..150ed21 100644 --- a/models/contact.go +++ b/models/contact.go @@ -40,8 +40,8 @@ func AddContact(m *Contact) (id int64, err error) { // GetContactById retrieves Contact by Id. Returns error if // Id doesn't exist -func GetContactById(id int) (v *Contact, err error) { - o := orm.NewOrm() +func GetContactById(o orm.Ormer, id int) (v *Contact, err error) { + //o := orm.NewOrm() v = &Contact{Id: id} if err = o.Read(v); err == nil { return v, nil @@ -51,9 +51,8 @@ func GetContactById(id int) (v *Contact, err error) { // GetAllContact retrieves all Contact matches certain condition. Returns empty list if // no records exist -func GetAllContact(query map[string]string, fields []string, sortby []string, order []string, +func GetAllContact(o orm.Ormer, query map[string]string, fields []string, sortby []string, order []string, offset int64, limit int64) (ml []interface{}, err error) { - o := orm.NewOrm() qs := o.QueryTable(new(Contact)) // query k=v for k, v := range query { diff --git a/services/companydb/companydb.go b/services/companydb.go similarity index 68% rename from services/companydb/companydb.go rename to services/companydb.go index 6501c9b..3f3a4f3 100644 --- a/services/companydb/companydb.go +++ b/services/companydb.go @@ -2,10 +2,12 @@ package services import ( "database/sql" + "errors" "fmt" "os" "github.com/astaxie/beego/orm" + jwt "github.com/dgrijalva/jwt-go" ) var dbs map[string]*sql.DB @@ -27,13 +29,30 @@ func InitCompanyService() { } // GetDatabase Get orm and user information -func GetDatabase(token string) { +func GetDatabase(tokenString string) (jwt.MapClaims, *sql.DB, error) { // validate token - // retrieve correct user/database - // check if open first - // try to open second - // return error otherwise + valid, token := Validate(tokenString) + if !valid { + return nil, nil, errors.New("Token is invalid") + } + + tokenMap := token.Claims.(jwt.MapClaims) + companyName := tokenMap["companyName"].(string) + + if dbs[companyName] != nil { + fmt.Println("DB Already open") + return tokenMap, dbs[companyName], nil + } + + conStr := fmt.Sprintf("host=127.0.0.1 port=5435 user=postgres password=postgre dbname=%s sslmode=disable", tokenMap["companyName"]) + fmt.Println(conStr) + db, err := sql.Open("postgres", conStr) + if err != nil { + return nil, nil, err + } + // return db with orm or error + return tokenMap, db, nil } // CreateDatabase Create a database by copying the template diff --git a/services/authentication/authentication.go b/services/jwt.go similarity index 80% rename from services/authentication/authentication.go rename to services/jwt.go index 49ca0d1..e698a69 100644 --- a/services/authentication/authentication.go +++ b/services/jwt.go @@ -9,18 +9,22 @@ import ( var hmacSecret []byte +// GenerateSecret generate the secret to verify JWTs func GenerateSecret() []byte { b := make([]byte, 32) rand.Read(b) return b } -func InitAuthService() { +// InitJWTService generate the secret to verify JWTs and store it in memory +func InitJWTService() { hmacSecret = GenerateSecret() + fmt.Println("InitJWTService", hmacSecret) // TODO: This needs to be replaced with reading rsa keys, there needs to be a automatic generation of these if they do not exist } +// Validate a jwt tokenstring func Validate(Token string) (bool, jwt.Token) { token, err := jwt.Parse(Token, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { @@ -31,6 +35,7 @@ func Validate(Token string) (bool, jwt.Token) { }) if err == nil && token.Valid { + fmt.Println("Token is valid") return true, *token } @@ -39,6 +44,7 @@ func Validate(Token string) (bool, jwt.Token) { return false, *token } +// CreateToken create a new jwt token with the provided claims func CreateToken(Claims jwt.MapClaims) string { // Create a new token object, specifying signing method and the claims diff --git a/tests/main.go b/tests/main.go index 50973b0..031cdb7 100644 --- a/tests/main.go +++ b/tests/main.go @@ -2,6 +2,7 @@ package test import ( _ "multitenantStack/routers" + auth "multitenantStack/services/authentication" "github.com/astaxie/beego" "github.com/astaxie/beego/orm" @@ -10,8 +11,7 @@ import ( func init() { orm.RegisterDataBase("default", "postgres", "host=127.0.0.1 port=5435 user=postgres password=postgre sslmode=disable") - orm.RegisterDataBase("company1", "postgres", "host=127.0.0.1 port=5435 user=postgres password=postgre dbname=company1 sslmode=disable") - orm.RegisterDataBase("company2", "postgres", "host=127.0.0.1 port=5435 user=postgres password=postgre dbname=company2 sslmode=disable") + auth.InitAuthService() orm.Debug = true beego.BConfig.WebConfig.DirectoryIndex = true