routebuilder_fasthttp.go 10.4 KiB
Newer Older
Roshan Patil's avatar
Roshan Patil committed
//  +build fasthttp

package routebuildermdl

import (
	"context"
Roshan Patil's avatar
Roshan Patil committed
	"strings"

	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/authmdl/jwtmdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/authmdl/roleenforcemdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/dalmdl"
Roshan Patil's avatar
Roshan Patil committed
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/errormdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/servicebuildermdl"
	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/statemdl"

Roshan Patil's avatar
Roshan Patil committed
	version "github.com/hashicorp/go-version"
	"github.com/pquerna/ffjson/ffjson"
	routing "github.com/qiangxue/fasthttp-routing"
Roshan Patil's avatar
Roshan Patil committed
	"github.com/tidwall/gjson"
)

// Init routing init
func Init(o, r, c *routing.RouteGroup, JWTKey string) {
	o.Post("/mql/state", statemdl.StateHandler)
	o.Post("/mql", OpenHandler)
	r.Post("/mql", RestrictedHandler)
	c.Post("/mql", RoleBasedHandler)
	o.Post("/heavymql", HeavyOpenHandler)
	r.Post("/heavymql", HeavyRestrictedHandler)
	c.Post("/heavymql", HeavyRoleBasedHandler)
	jwtmdl.GlobalJWTKey = JWTKey
}

func commonHandler(c *routing.Context, isRestricted, isRoleBased, heavyDataActivity bool, principalObj servicebuildermdl.Principal) error {
	serviceHeader := string(c.Request.Header.Peek("Service-Header"))
	services := strings.Split(serviceHeader, ",")
	versionError := appVersioning(c)
	if versionError != nil {
		_, err := c.WriteString(versionError.Error())
		c.SetStatusCode(417)
		return err
	}
	responseMap := make(map[string]responseData)
	var reqBody []byte

	reqBody = c.Request.Body()
	requestBody := gjson.ParseBytes(reqBody)
	for i := 0; i < len(services); i++ {
		responseDataObj := responseData{}
		service := services[i]
Kunal Taitkar's avatar
Kunal Taitkar committed
		result, ab, isCompressed, errorCode, err := executeService(service, []byte(requestBody.Get(service).String()), isRestricted, isRoleBased, heavyDataActivity, principalObj)
Roshan Patil's avatar
Roshan Patil committed
		if errormdl.CheckErr1(err) != nil {
			if ab == nil {
				responseDataObj.ErrorCode = errorCode
				responseDataObj.Error = err.Error()
			} else {
				responseDataObj.Error = ab.GetErrorData()
				if responseDataObj.Error == nil {
					responseDataObj.Error = err.Error()
				}
				errorCode := ab.GetErrorCode()
				if errorCode == 0 {
					errorCode = errormdl.EXPECTATIONFAILED
				}
				responseDataObj.ErrorCode = errorCode
				if ab.TransactionEnable {
					loggermdl.LogError("transaction enabled rollback")

					var err error
					// database transaction rollback if transaction is enabled
					switch ab.DatabaseType {
					case dalmdl.MYSQL:
						if ab.TXN != nil {
							loggermdl.LogError("MYSQL Transaction Rollbacked")
							err = ab.TXN.Rollback()
							if err != nil {
								responseDataObj.Error = err.Error()
								responseDataObj.ErrorCode = errormdl.MYSQLERROR
							}
Roshan Patil's avatar
Roshan Patil committed
						}
Kunal Taitkar's avatar
Kunal Taitkar committed

					case dalmdl.SQLSERVER:
						if ab.SQLServerTXN != nil {
							loggermdl.LogError("SQLSERVER Transaction Rollbacked")
							err = ab.SQLServerTXN.Rollback()
							if err != nil {
								responseDataObj.Error = err.Error()
								responseDataObj.ErrorCode = errormdl.MYSQLERROR
							}
					case dalmdl.GraphDB:
						if ab.GraphDbTXN != nil {
							loggermdl.LogError("GRAPHDB Transaction Rollbacked")
							err = ab.GraphDbTXN.Discard(context.TODO())
							if err != nil {
								responseDataObj.Error = err.Error()
								responseDataObj.ErrorCode = errormdl.GRAPHDBERROR
							}
						}

					default:
						loggermdl.LogError("Invalid database type while rollback transaction")
Kunal Taitkar's avatar
Kunal Taitkar committed

					}
Roshan Patil's avatar
Roshan Patil committed
				}
			}
		} else {

			if ab != nil {
				if ab.TransactionEnable {
					var err error

					switch ab.DatabaseType {
					case dalmdl.MYSQL:
						if ab.TXN != nil {
							loggermdl.LogError("MYSQL Transaction Commit")
							err = ab.TXN.Commit()
							if err != nil {
								responseDataObj.Error = err.Error()
								responseDataObj.ErrorCode = errormdl.MYSQLERROR
							} else {
								responseDataObj.Result = result
								responseDataObj.ErrorCode = errormdl.NOERROR
							}
Roshan Patil's avatar
Roshan Patil committed
						}
					case dalmdl.SQLSERVER:
						if ab.SQLServerTXN != nil {
							loggermdl.LogError("SQLSERVER Transaction Commit")
							err = ab.SQLServerTXN.Commit()
							if err != nil {
								responseDataObj.Error = err.Error()
								responseDataObj.ErrorCode = errormdl.SQLSERVERERROR
							} else {
								responseDataObj.Result = result
								responseDataObj.ErrorCode = errormdl.NOERROR
							}
					case dalmdl.GraphDB:
						if ab.SQLServerTXN != nil {
							loggermdl.LogError("GRAPHDB Transaction Commit")
							err = ab.GraphDbTXN.Commit(context.TODO())
							if err != nil {
								responseDataObj.Error = err.Error()
								responseDataObj.ErrorCode = errormdl.GRAPHDBERROR
							} else {
								responseDataObj.Result = result
								responseDataObj.ErrorCode = errormdl.NOERROR
							}
						}

					default:
						loggermdl.LogError("Invalid database type while commit transaction")
Roshan Patil's avatar
Roshan Patil committed
					}
Roshan Patil's avatar
Roshan Patil committed
				} else {
					responseDataObj.Result = result
					responseDataObj.ErrorCode = errormdl.NOERROR
				}
			} else {
				responseDataObj.Result = result
				responseDataObj.ErrorCode = errormdl.NOERROR
			}
		}
		responseDataObj.IsCompressed = isCompressed
		responseDataObj = formatResponse(ab, responseDataObj)
		responseMap[service] = responseDataObj
		// Token extraction
		// if ab != nil {
		// 	token, ok := ab.GetDataString("MQLToken")
		// 	if !ok {
		// 		token = string(c.Request.Header.Peek("Authorization"))
		// 		token = strings.TrimPrefix(token, "Bearer")
		// 		token = strings.TrimSpace(token)
		// 		c.Response.Header.Set("Authorization", token)
		// 	}
		// 	c.Response.Header.Set("Authorization", token)
		// } else {
		// 	token := string(c.Request.Header.Peek("Authorization"))
		// 	token = strings.TrimPrefix(token, "Bearer")
		// 	token = strings.TrimSpace(token)
		// 	c.Response.Header.Set("Authorization", token)
		// }

Roshan Patil's avatar
Roshan Patil committed
		if ab != nil {
			token, ok := ab.GetDataString("MQLToken")
Roshan Patil's avatar
Roshan Patil committed
				c.Response.Header.Set("Authorization", token)
			}
		}
	}
	ba, _ := ffjson.Marshal(responseMap)
	_, err := c.Write(ba)
	c.SetStatusCode(200)
	return err
}

// OpenHandler for /o
func OpenHandler(c *routing.Context) error {
	c.Response.Header.Set("content-type", "application/json")
	principal := servicebuildermdl.Principal{}

	principal.ClientIP = c.RemoteIP().String()
	commonHandler(c, false, false, false, principal)
	return nil
}

// RestrictedHandler for /r
func RestrictedHandler(c *routing.Context) error {
	c.Response.Header.Set("content-type", "application/json")
	pricipalObj, extractError := extractPricipalObject(c)
	if extractError != nil {
		loggermdl.LogError(extractError)
		_, err := c.WriteString(extractError.Error())
		c.SetStatusCode(412)
		return err
	}
	pricipalObj.ClientIP = c.RemoteIP().String()
	commonHandler(c, true, false, false, pricipalObj)
	return nil
}

// RoleBasedHandler for /r/c
func RoleBasedHandler(c *routing.Context) error {
	c.Response.Header.Set("content-type", "application/json")
	pricipalObj, extractError := extractPricipalObject(c)
	if extractError != nil {
		loggermdl.LogError(extractError)
		_, err := c.WriteString(extractError.Error())
		c.SetStatusCode(412)
		return err
	}
	pricipalObj.ClientIP = c.RemoteIP().String()
	commonHandler(c, true, true, false, pricipalObj)
	return nil
}

// HeavyOpenHandler for /o
func HeavyOpenHandler(c *routing.Context) error {
	c.Response.Header.Set("content-type", "application/json")
	principal := servicebuildermdl.Principal{}

	principal.ClientIP = c.RemoteIP().String()
	commonHandler(c, false, false, true, principal)
	return nil
}

// HeavyRestrictedHandler for /r
func HeavyRestrictedHandler(c *routing.Context) error {
	c.Response.Header.Set("content-type", "application/json")
	pricipalObj, extractError := extractPricipalObject(c)
	if extractError != nil {
		loggermdl.LogError(extractError)
		_, err := c.WriteString(extractError.Error())
		c.SetStatusCode(412)
		return err
	}
	pricipalObj.ClientIP = c.RemoteIP().String()
	commonHandler(c, true, false, true, pricipalObj)
	return nil
}

// HeavyRoleBasedHandler for /r/c
func HeavyRoleBasedHandler(c *routing.Context) error {
	c.Response.Header.Set("content-type", "application/json")
	pricipalObj, extractError := extractPricipalObject(c)
	if extractError != nil {
		loggermdl.LogError(extractError)
		_, err := c.WriteString(extractError.Error())
		c.SetStatusCode(412)
		return err
	}
	pricipalObj.ClientIP = c.RemoteIP().String()
	commonHandler(c, true, true, true, pricipalObj)
	return nil
}

func appVersioning(c *routing.Context) error {
	if isAppVersionEnabled {
		appVersion := string(c.Request.Header.Peek("app-version"))
		if appVersion == "" {
			return errormdl.Wrap("No App version Found in request header")
		}
		ver, err := version.NewVersion(appVersion)
		if errormdl.CheckErr(err) != nil {
			return errormdl.CheckErr(err)
		}
		if isStrictMode {
			if !ver.Equal(applicationVersion) {
				return errormdl.Wrap("Application version mismatched")
			}
		} else {
			if ver.GreaterThan(applicationVersion) {
				return errormdl.Wrap("Server Version is outdated")
			}
			if ver.LessThan(minimumSupportedVersion) {
				return errormdl.Wrap("Client Version is outdated")
			}
		}
	}
	return nil
}

func extractPricipalObject(c *routing.Context) (servicebuildermdl.Principal, error) {
	principal := servicebuildermdl.Principal{}
	if jwtmdl.GlobalJWTKey == "" {
		return principal, errormdl.Wrap("No Global JWT key found")
	}

	claim, decodeError := jwtmdl.DecodeToken(&c.Request)
	if errormdl.CheckErr(decodeError) != nil {
		// loggermdl.LogError(decodeError)
Roshan Patil's avatar
Roshan Patil committed
		return principal, errormdl.CheckErr(decodeError)
	}

	groups, grperr := roleenforcemdl.GetGroupNames(claim, "groups")
	if errormdl.CheckErr(grperr) != nil {
		loggermdl.LogError(grperr)
		return principal, errormdl.CheckErr(grperr)
	}
	userID, _ := claim["userId"].(string)
	// if !ok {
	// 	loggermdl.LogError("Unable to parse UserID from JWT Token")
	// 	return principal, errormdl.Wrap("Unable to parse UserID from JWT Token")
	// }

	if len(userID) < 2 {
		loggermdl.LogError("UserID length is less than 2")
		return principal, errormdl.Wrap("UserID length is less than 2")
	}

Roshan Patil's avatar
Roshan Patil committed
	rawMetadata, ok := claim["metadata"]
	if ok {
		metadata, ok := rawMetadata.(string)
		if !ok {
			loggermdl.LogError("Unable to parse metadata from JWT Token")
			return principal, errormdl.Wrap("Unable to parse metadata from JWT Token")
		}
		principal.Metadata = metadata
	}
	principal.Groups = groups
	principal.UserID = userID
	principal.Token = string(c.Request.Header.Peek("Authorization"))
	return principal, nil
}