multitenantStack/controllers/auth.go

265 lines
6.3 KiB
Go

package controllers
import (
"fmt"
"multitenantStack/models"
companydb "multitenantStack/services/companydb"
"strings"
constants "multitenantStack/constants"
tokenTools "multitenantStack/services/tokenTools"
"time"
"github.com/astaxie/beego/orm"
jwt "github.com/dgrijalva/jwt-go"
"github.com/kennygrant/sanitize"
)
// AuthController operations for Auth
type AuthController struct {
BaseController
}
// AuthResponse the format for all responses from auth
type AuthResponse struct {
Status int `json:"status"`
Jwt string `json:"jwt"`
User models.CompanyUser `json:"user"`
}
// URLMapping ...
func (c *AuthController) URLMapping() {
// This block is used to drastically speed up the annotation -> lookup process
c.Mapping("Login", c.Login)
c.Mapping("Register", c.Register)
}
// Login Get a JWT token for the user
// @Title Create
// @Description create Auth
// @Param body body models.Auth true "body for Auth content"
// @Success 201 {object} models.Auth
// @Failure 403 body is empty
// @router /login [post]
func (c *AuthController) Login() {
if c.Ctx.Input.Method() != "POST" {
c.ServeJSONError("Method not allowed")
return
}
tokenHeader := c.Ctx.Request.Header.Get("X-JWTtoken")
if tokenHeader != "" {
valid, _ := tokenTools.Validate(tokenHeader)
if valid {
c.ServeJSONError("You are already logged in")
return
}
}
email := c.GetString("email")
password := c.GetString("password")
if email == "" || password == "" {
c.ServeJSONError("Email/Password missing")
return
}
systemdb := companydb.GetSystemDatabase()
if systemdb == nil {
c.ServeJSONError("Error retrieving User")
return
}
o, err := orm.NewOrmWithDB("postgres", "default", systemdb)
if err != nil {
c.ServeJSONError("Error retrieving User")
return
}
userCompanyMapping, err := models.GetUserCompanyMapByEmail(o, email)
if err != nil {
c.ServeJSONError("Error retrieving User")
return
}
if !tokenTools.CheckPasswordHash(password, userCompanyMapping.PasswordHash) {
c.ServeJSONError("Email/Password incorrect")
return
}
companyName := userCompanyMapping.Company
companyUserID := userCompanyMapping.CompanyUserID
db, err := companydb.GetDatabaseWithName(companyName)
if err != nil {
c.ServeJSONError("Error retrieving Company")
return
}
o, err = orm.NewOrmWithDB("postgres", "default", db)
if err != nil {
c.ServeJSONError("Error retrieving CompanyData")
return
}
companyUser, err := models.GetCompanyUserById(o, int(companyUserID))
if err != nil {
c.ServeJSONError("Error retrieving Company User")
return
}
tokenString := ""
if email == "admin@admin.at" && password == "my password" {
// The jwtClaims are our trusted clientside session
tokenString = tokenTools.CreateToken(jwt.MapClaims{
"email": email,
"companyName": companyName,
"companyUserID": companyUserID,
"exp": time.Now().Unix() + 3600,
})
} else {
c.ServeJSONError("Invalid user/password")
return
}
json := AuthResponse{200, tokenString, *companyUser}
c.Data["json"] = &json
c.ServeJSON()
}
// Register Register a new company and user, create DB and so on
// @Title Create
// @Description create Auth
// @Param body body models.Auth true "body for Auth content"
// @Success 201 {object} models.Auth
// @Failure 403 body is empty
// @router /register [post]
func (c *AuthController) Register() {
// can be called without jwt token set
// needed data:
// email
// password
// companyname
// optional: username
// Tasks:
// create database
// create user_company_map entry
// create company user
// return jwt token and user profile
if c.Ctx.Input.Method() != "POST" {
c.ServeJSONError("Method not allowed")
return
}
tokenHeader := c.Ctx.Request.Header.Get("X-JWTtoken")
if tokenHeader != "" {
valid, _ := tokenTools.Validate(tokenHeader)
if valid {
c.ServeJSONError("You are already logged in")
return
}
}
email := c.GetString("email")
password := c.GetString("password")
username := c.GetString("username")
companyname := c.GetString("companyname")
companyname = sanitize.BaseName(companyname)
companyname = strings.ToLower(companyname)
companyname = fmt.Sprintf("company_%s", companyname)
if email == "" || password == "" || companyname == "" || username == "" {
c.ServeJSONError("Email/Password/Companyname missing")
return
}
systemdb := companydb.GetSystemDatabase()
if systemdb == nil {
c.ServeJSONError("Error retrieving data")
return
}
o, err := orm.NewOrmWithDB("postgres", "default", systemdb)
if err != nil {
c.ServeJSONError("Error retrieving data")
return
}
ucmExists, err := models.GetUserCompanyMapByEmail(o, email)
if ucmExists != nil {
fmt.Println(ucmExists)
c.ServeJSONError("Error: Email exists!")
return
}
if companydb.HasDatabase(companyname) {
c.ServeJSONError("Error: Company exists!")
return
}
var userCompanyMapping models.UserCompanyMap
newHash, _ := tokenTools.HashPassword(password)
userCompanyMapping.PasswordHash = newHash
userCompanyMapping.Company = companyname
userCompanyMapping.Email = email
ucmID, err := models.AddUserCompanyMap(o, &userCompanyMapping)
if err != nil {
c.ServeJSONErrorWithError("Error on saving user", err)
return
}
// Create company
newDB, err := companydb.CreateDatabase(companyname)
if err != nil {
c.ServeJSONErrorWithError("Error on creating DB", err)
return
}
newO, err := orm.NewOrmWithDB("postgres", "default", newDB)
if err != nil {
c.ServeJSONErrorWithError("Error retrieving company data", err)
return
}
var companyUser models.CompanyUser
companyUser.Name = username
companyUser.Profile = "{}"
companyUser.Role = constants.RoleOwner
userID, err := models.AddCompanyUser(newO, &companyUser)
if err != nil {
c.ServeJSONError("Error on saving company user")
return
}
// edit usermapping
userCompanyMapping.ID = int(ucmID)
userCompanyMapping.CompanyUserID = int16(userID)
if err := models.UpdateUserCompanyMapById(o, &userCompanyMapping); err != nil {
c.ServeJSONError("Error on saving user")
return
}
tokenString := ""
tokenString = tokenTools.CreateToken(jwt.MapClaims{
"email": email,
"companyName": companyname,
"companyUserID": userCompanyMapping.CompanyUserID,
"exp": time.Now().Unix() + 3600,
})
json := AuthResponse{200, tokenString, companyUser}
c.Data["json"] = &json
c.ServeJSON()
}