<commit_before>package api

import (
	"aguin/crypto"
	"aguin/model"
	"aguin/utils"
	"aguin/validator"
	"aguin/config"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"time"
)

type RequestData struct {
	crypted bool
	app     model.Application
	message map[string]interface{}
}

type AguinSetting struct {
	dbSession *mgo.Session
	log       *utils.Logger // General log
}

func VerifyRequest() interface{} {
	return func(c martini.Context, res http.ResponseWriter, req *http.Request, render render.Render) {
		log := utils.GetLogger("aguin")
		// Recover from panic and serve internal server error in json format
		defer func() {
			if err := recover(); err != nil {
				stack := utils.Stack(3)
				log.Error("PANIC: %s\n%s", err, stack)
				serveInternalServerError(render)
			}
		}()
	
		apiKey := req.Header.Get("X-AGUIN-API-KEY")

		if apiKey == "" || !validator.ValidAPIKey(apiKey) {
			serveForbidden(render)
			return
		}
		// If empty then data must be encrypted
		apiSecret := req.Header.Get("X-AGUIN-API-SECRET")

		app := model.Application{}

		setting := AguinSetting{}
		requestData := RequestData{}
		setting.log = log
		setting.dbSession = model.Session()
		defer setting.dbSession.Close()
		err := model.AppCollection(setting.dbSession).FindId(bson.ObjectIdHex(apiKey)).One(&app)
		if err != nil {
			serveForbidden(render)
			return
		}

		requestData.app = app

		// parse form for data
		req.ParseForm()
		if config.AppConf().EncryptionEnabled {
			// decrypting data here
			authKey := []byte(app.Secret)
			decryptedMessage, err := crypto.Decrypt(req.Form.Get("message"), authKey, authKey)
			if err != nil {
				setting.log.Error("error: %v, message: %v", err, req.Form.Get("message"))
				serveInternalServerError(render)
				return
			}
			requestData.message = decryptedMessage.(map[string]interface{})
			requestData.crypted = true
		} else {
			// have secret, compare with the one in database
			if apiSecret != app.Secret {
				serveForbidden(render)
				return
			}
			data3, err := utils.Bytes2json([]byte(req.Form.Get("message")))
			if err != nil {
				setting.log.Error("%v", err)
				serveBadRequestJson(render)
				return
			}
			requestData.message = data3.(map[string]interface{})
			requestData.crypted = false
		}
		c.Map(requestData)
		c.Map(setting)
		c.Next()
	}
}

func IndexGet(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting AguinSetting) {
	criteria := validator.ValidateSearch(requestData.message)
	if criteria.Validated == false {
		setting.log.Info("%v", requestData.message)
		serveBadRequestData(render)
		return
	}
	var results []model.Entity
	err := model.EntityCollection(setting.dbSession).Find(bson.M{"name": criteria.Entity, "appid": requestData.app.Id}).All(&results)
	if err != nil {
		setting.log.Error("%v", err)
		serveInternalServerError(render)
		return
	}
	ServeResponse(http.StatusOK, render, results, requestData, setting)
}

func IndexPost(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting AguinSetting) {
	result := validator.ValidateEntity(requestData.message)
	if result.Validated && result.Entity != "" {
		doc := model.Entity{}
		doc.Name = result.Entity
		doc.AppId = requestData.app.Id
		doc.CreatedAt = time.Now()
		doc.Data = result.Data
		err := doc.Save(setting.dbSession)
		if err == nil {
			serveOK(render)
			return
		} else {
			setting.log.Error("%v", err)
			serveInternalServerError(render)
			return
		}
	}
	setting.log.Info("%v", requestData.message)
	serveBadRequestData(render)
}

func IndexStatus(render render.Render) {
	serveOK(render)
}

func NotFound(render render.Render) {
	serveNotFound(render)
}

/* Serving responses */
func ServeResponse(status int, render render.Render, result interface{}, requestData RequestData, setting AguinSetting) {
	if requestData.crypted {
		ServeSignedResponse(status, render, result, requestData, setting)
	} else {
		ServeUnsignedResponse(status, render, result, requestData, setting)
	}
}

func ServeUnsignedResponse(status int, render render.Render, result interface{}, requestData RequestData, setting AguinSetting) {
	render.JSON(status, map[string]interface{}{"result": result, "encrypted": false})
}

func ServeSignedResponse(status int, render render.Render, result interface{}, requestData RequestData, setting AguinSetting) {
	authKey := []byte(requestData.app.Secret)
	cryptedResult, err := crypto.Encrypt(result, authKey, authKey)
	if err != nil {
		serveInternalServerError(render)
		return
	}
	render.JSON(status, map[string]interface{}{"result": cryptedResult, "encrypted": true})
}

func serveOK(render render.Render) {
	render.JSON(http.StatusOK, map[string]interface{}{"result": map[string]interface{}{"status": "OK"}, "encrypted": false})
}

func serveForbidden(render render.Render) {
	render.JSON(http.StatusForbidden, map[string]interface{}{"result": map[string]interface{}{"status": "FORBIDDEN"}, "encrypted": false})
}

func serveBadRequestData(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Invalid data"}, "encrypted": false})
}

func serveBadRequestJson(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Invalid json"}, "encrypted": false})
}

func serveInternalServerError(render render.Render) {
	render.JSON(http.StatusInternalServerError, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Internal server error"}, "encrypted": false})
}

func serveNotFound(render render.Render) {
	render.JSON(http.StatusNotFound, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Not found"}, "encrypted": false})
}
<commit_msg>Encryption or not determined from application config instead.<commit_after>package api

import (
	"aguin/config"
	"aguin/crypto"
	"aguin/model"
	"aguin/utils"
	"aguin/validator"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"time"
)

type RequestData struct {
	app     model.Application
	message map[string]interface{}
}

type AguinSetting struct {
	dbSession *mgo.Session
	log       *utils.Logger // General log
	conf      *config.AppConfig
}

func VerifyRequest() interface{} {
	return func(c martini.Context, res http.ResponseWriter, req *http.Request, render render.Render) {
		log := utils.GetLogger("aguin")
		// Recover from panic and serve internal server error in json format
		defer func() {
			if err := recover(); err != nil {
				stack := utils.Stack(3)
				log.Error("PANIC: %s\n%s", err, stack)
				serveInternalServerError(render)
			}
		}()

		apiKey := req.Header.Get("X-AGUIN-API-KEY")

		if apiKey == "" || !validator.ValidAPIKey(apiKey) {
			serveForbidden(render)
			return
		}
		// If empty then data must be encrypted
		apiSecret := req.Header.Get("X-AGUIN-API-SECRET")

		app := model.Application{}

		setting := AguinSetting{}
		requestData := RequestData{}
		setting.log = log
		setting.dbSession = model.Session()
		defer setting.dbSession.Close()
		err := model.AppCollection(setting.dbSession).FindId(bson.ObjectIdHex(apiKey)).One(&app)
		if err != nil {
			serveForbidden(render)
			return
		}

		requestData.app = app
		setting.conf = config.AppConf()
		// parse form for data
		req.ParseForm()
		if setting.conf.EncryptionEnabled {
			// decrypting data here
			authKey := []byte(app.Secret)
			decryptedMessage, err := crypto.Decrypt(req.Form.Get("message"), authKey, authKey)
			if err != nil {
				setting.log.Error("error: %v, message: %v", err, req.Form.Get("message"))
				serveInternalServerError(render)
				return
			}
			requestData.message = decryptedMessage.(map[string]interface{})
		} else {
			// have secret, compare with the one in database
			if apiSecret != app.Secret {
				serveForbidden(render)
				return
			}
			data3, err := utils.Bytes2json([]byte(req.Form.Get("message")))
			if err != nil {
				setting.log.Error("%v", err)
				serveBadRequestJson(render)
				return
			}
			requestData.message = data3.(map[string]interface{})
		}
		c.Map(requestData)
		c.Map(setting)
		c.Next()
	}
}

func IndexGet(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting AguinSetting) {
	criteria := validator.ValidateSearch(requestData.message)
	if criteria.Validated == false {
		setting.log.Info("%v", requestData.message)
		serveBadRequestData(render)
		return
	}
	var results []model.Entity
	err := model.EntityCollection(setting.dbSession).Find(bson.M{"name": criteria.Entity, "appid": requestData.app.Id}).All(&results)
	if err != nil {
		setting.log.Error("%v", err)
		serveInternalServerError(render)
		return
	}
	ServeResponse(http.StatusOK, render, results, requestData, setting)
}

func IndexPost(res http.ResponseWriter, req *http.Request, render render.Render, requestData RequestData, setting AguinSetting) {
	result := validator.ValidateEntity(requestData.message)
	if result.Validated && result.Entity != "" {
		doc := model.Entity{}
		doc.Name = result.Entity
		doc.AppId = requestData.app.Id
		doc.CreatedAt = time.Now()
		doc.Data = result.Data
		err := doc.Save(setting.dbSession)
		if err == nil {
			serveOK(render)
			return
		} else {
			setting.log.Error("%v", err)
			serveInternalServerError(render)
			return
		}
	}
	setting.log.Info("%v", requestData.message)
	serveBadRequestData(render)
}

func IndexStatus(render render.Render) {
	serveOK(render)
}

func NotFound(render render.Render) {
	serveNotFound(render)
}

/* Serving responses */
func ServeResponse(status int, render render.Render, result interface{}, requestData RequestData, setting AguinSetting) {
	if setting.conf.EncryptionEnabled {
		ServeSignedResponse(status, render, result, requestData, setting)
	} else {
		ServeUnsignedResponse(status, render, result, requestData, setting)
	}
}

func ServeUnsignedResponse(status int, render render.Render, result interface{}, requestData RequestData, setting AguinSetting) {
	render.JSON(status, map[string]interface{}{"result": result, "encrypted": false})
}

func ServeSignedResponse(status int, render render.Render, result interface{}, requestData RequestData, setting AguinSetting) {
	authKey := []byte(requestData.app.Secret)
	cryptedResult, err := crypto.Encrypt(result, authKey, authKey)
	if err != nil {
		serveInternalServerError(render)
		return
	}
	render.JSON(status, map[string]interface{}{"result": cryptedResult, "encrypted": true})
}

func serveOK(render render.Render) {
	render.JSON(http.StatusOK, map[string]interface{}{"result": map[string]interface{}{"status": "OK"}, "encrypted": false})
}

func serveForbidden(render render.Render) {
	render.JSON(http.StatusForbidden, map[string]interface{}{"result": map[string]interface{}{"status": "FORBIDDEN"}, "encrypted": false})
}

func serveBadRequestData(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Invalid data"}, "encrypted": false})
}

func serveBadRequestJson(render render.Render) {
	render.JSON(http.StatusBadRequest, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Invalid json"}, "encrypted": false})
}

func serveInternalServerError(render render.Render) {
	render.JSON(http.StatusInternalServerError, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Internal server error"}, "encrypted": false})
}

func serveNotFound(render render.Render) {
	render.JSON(http.StatusNotFound, map[string]interface{}{"result": map[string]interface{}{"status": "ERROR", "msg": "Not found"}, "encrypted": false})
}

{'fraction_non_alphanumeric': 0.14531500379522644, 'fraction_numerical': 0.001012060386269714, 'mean_word_length': 4.997976732422863, 'pattern_counts': {'":': 56, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2057089', 'n_tokens_mistral': 4074, 'n_tokens_neox': 3805, 'n_words': 799}