mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
Merge branch 'refactor-amqp-session' into 'master'
Refactor AMQP client See merge request acs/public/villas/web-backend-go!37
This commit is contained in:
commit
0269e31fa6
52 changed files with 2164 additions and 1653 deletions
|
@ -111,3 +111,67 @@ func generatePassword(Len int) string {
|
||||||
|
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add test users defined above
|
||||||
|
func AddTestUsers() error {
|
||||||
|
|
||||||
|
testUsers := []User{User0, UserA, UserB, UserC}
|
||||||
|
DBpool.AutoMigrate(&User{})
|
||||||
|
|
||||||
|
for _, user := range testUsers {
|
||||||
|
err := DBpool.Create(&user).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credentials
|
||||||
|
var StrPassword0 = "xyz789"
|
||||||
|
var StrPasswordA = "abc123"
|
||||||
|
var StrPasswordB = "bcd234"
|
||||||
|
var StrPasswordC = "guestpw"
|
||||||
|
|
||||||
|
// Hash passwords with bcrypt algorithm
|
||||||
|
var bcryptCost = 10
|
||||||
|
var pw0, _ = bcrypt.GenerateFromPassword([]byte(StrPassword0), bcryptCost)
|
||||||
|
var pwA, _ = bcrypt.GenerateFromPassword([]byte(StrPasswordA), bcryptCost)
|
||||||
|
var pwB, _ = bcrypt.GenerateFromPassword([]byte(StrPasswordB), bcryptCost)
|
||||||
|
var pwC, _ = bcrypt.GenerateFromPassword([]byte(StrPasswordC), bcryptCost)
|
||||||
|
|
||||||
|
var User0 = User{Username: "User_0", Password: string(pw0),
|
||||||
|
Role: "Admin", Mail: "User_0@example.com"}
|
||||||
|
var UserA = User{Username: "User_A", Password: string(pwA),
|
||||||
|
Role: "User", Mail: "User_A@example.com", Active: true}
|
||||||
|
var UserB = User{Username: "User_B", Password: string(pwB),
|
||||||
|
Role: "User", Mail: "User_B@example.com", Active: true}
|
||||||
|
var UserC = User{Username: "User_C", Password: string(pwC),
|
||||||
|
Role: "Guest", Mail: "User_C@example.com", Active: true}
|
||||||
|
|
||||||
|
type Credentials struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var AdminCredentials = Credentials{
|
||||||
|
Username: User0.Username,
|
||||||
|
Password: StrPassword0,
|
||||||
|
}
|
||||||
|
|
||||||
|
var UserACredentials = Credentials{
|
||||||
|
Username: UserA.Username,
|
||||||
|
Password: StrPasswordA,
|
||||||
|
}
|
||||||
|
|
||||||
|
var UserBCredentials = Credentials{
|
||||||
|
Username: UserB.Username,
|
||||||
|
Password: StrPasswordB,
|
||||||
|
}
|
||||||
|
|
||||||
|
var GuestCredentials = Credentials{
|
||||||
|
Username: UserC.Username,
|
||||||
|
Password: StrPasswordC,
|
||||||
|
}
|
||||||
|
|
296
database/permissions.go
Normal file
296
database/permissions.go
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
/** Database package, roles.
|
||||||
|
*
|
||||||
|
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
||||||
|
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||||
|
* @license GNU General Public License (version 3)
|
||||||
|
*
|
||||||
|
* VILLASweb-backend-go
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************************/
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckScenarioPermissions(c *gin.Context, operation CRUD, scenarioIDsource string, scenarioIDbody int) (bool, Scenario) {
|
||||||
|
|
||||||
|
var so Scenario
|
||||||
|
|
||||||
|
err := ValidateRole(c, ModelScenario, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of scenario failed): %v", err))
|
||||||
|
return false, so
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation == Create || (operation == Read && scenarioIDsource == "none") {
|
||||||
|
return true, so
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarioID, err := helper.GetIDOfElement(c, "scenarioID", scenarioIDsource, scenarioIDbody)
|
||||||
|
if err != nil {
|
||||||
|
return false, so
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, _ := c.Get(UserIDCtx)
|
||||||
|
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&so, uint(scenarioID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, so
|
||||||
|
}
|
||||||
|
|
||||||
|
u := User{}
|
||||||
|
err = db.Find(&u, userID.(uint)).Error
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, "Access denied (user has no access or scenario is locked).")
|
||||||
|
return false, so
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Role == "Admin" {
|
||||||
|
return true, so
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarioUser := User{}
|
||||||
|
err = db.Order("ID asc").Model(&so).Where("ID = ?", userID.(uint)).Related(&scenarioUser, "Users").Error
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, "Access denied (user has no access or scenario is locked).")
|
||||||
|
return false, so
|
||||||
|
}
|
||||||
|
|
||||||
|
if !scenarioUser.Active {
|
||||||
|
helper.UnprocessableEntityError(c, "Access denied (user has no access or scenario is locked).")
|
||||||
|
return false, so
|
||||||
|
} else if so.IsLocked && operation != Read {
|
||||||
|
helper.UnprocessableEntityError(c, "Access denied (user has no access or scenario is locked).")
|
||||||
|
return false, so
|
||||||
|
} else {
|
||||||
|
return true, so
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckComponentConfigPermissions(c *gin.Context, operation CRUD, configIDSource string, configIDBody int) (bool, ComponentConfiguration) {
|
||||||
|
|
||||||
|
var m ComponentConfiguration
|
||||||
|
|
||||||
|
err := ValidateRole(c, ModelComponentConfiguration, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of Component Configuration failed): %v", err.Error()))
|
||||||
|
return false, m
|
||||||
|
}
|
||||||
|
|
||||||
|
configID, err := helper.GetIDOfElement(c, "configID", configIDSource, configIDBody)
|
||||||
|
if err != nil {
|
||||||
|
return false, m
|
||||||
|
}
|
||||||
|
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&m, uint(configID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, m
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, _ := CheckScenarioPermissions(c, operation, "body", int(m.ScenarioID))
|
||||||
|
if !ok {
|
||||||
|
return false, m
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, m
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckSignalPermissions(c *gin.Context, operation CRUD) (bool, Signal) {
|
||||||
|
|
||||||
|
var sig Signal
|
||||||
|
|
||||||
|
err := ValidateRole(c, ModelSignal, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of signal failed): %v", err.Error()))
|
||||||
|
return false, sig
|
||||||
|
}
|
||||||
|
|
||||||
|
signalID, err := helper.GetIDOfElement(c, "signalID", "path", -1)
|
||||||
|
if err != nil {
|
||||||
|
return false, sig
|
||||||
|
}
|
||||||
|
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&sig, uint(signalID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, sig
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, _ := CheckComponentConfigPermissions(c, operation, "body", int(sig.ConfigID))
|
||||||
|
if !ok {
|
||||||
|
return false, sig
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, sig
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckDashboardPermissions(c *gin.Context, operation CRUD, dabIDSource string, dabIDBody int) (bool, Dashboard) {
|
||||||
|
|
||||||
|
var dab Dashboard
|
||||||
|
|
||||||
|
err := ValidateRole(c, ModelDashboard, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation failed): %v", err.Error()))
|
||||||
|
return false, dab
|
||||||
|
}
|
||||||
|
|
||||||
|
dabID, err := helper.GetIDOfElement(c, "dashboardID", dabIDSource, dabIDBody)
|
||||||
|
if err != nil {
|
||||||
|
return false, dab
|
||||||
|
}
|
||||||
|
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&dab, uint(dabID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, dab
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, _ := CheckScenarioPermissions(c, operation, "body", int(dab.ScenarioID))
|
||||||
|
if !ok {
|
||||||
|
return false, dab
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, dab
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckWidgetPermissions(c *gin.Context, operation CRUD, widgetIDBody int) (bool, Widget) {
|
||||||
|
|
||||||
|
var w Widget
|
||||||
|
var err error
|
||||||
|
err = ValidateRole(c, ModelWidget, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of widget failed): %v", err.Error()))
|
||||||
|
return false, w
|
||||||
|
}
|
||||||
|
|
||||||
|
var widgetID int
|
||||||
|
if widgetIDBody < 0 {
|
||||||
|
widgetID, err = helper.GetIDOfElement(c, "widgetID", "path", -1)
|
||||||
|
if err != nil {
|
||||||
|
return false, w
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
widgetID = widgetIDBody
|
||||||
|
}
|
||||||
|
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&w, uint(widgetID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, w
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, _ := CheckDashboardPermissions(c, operation, "body", int(w.DashboardID))
|
||||||
|
if !ok {
|
||||||
|
return false, w
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, w
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckFilePermissions(c *gin.Context, operation CRUD) (bool, File) {
|
||||||
|
|
||||||
|
var f File
|
||||||
|
|
||||||
|
err := ValidateRole(c, ModelFile, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of file failed): %v", err.Error()))
|
||||||
|
return false, f
|
||||||
|
}
|
||||||
|
|
||||||
|
fileID, err := helper.GetIDOfElement(c, "fileID", "path", -1)
|
||||||
|
if err != nil {
|
||||||
|
return false, f
|
||||||
|
}
|
||||||
|
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&f, uint(fileID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, f
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation != Read {
|
||||||
|
// check access to scenario only if operation is not Read (=download) of file
|
||||||
|
ok, _ := CheckScenarioPermissions(c, operation, "body", int(f.ScenarioID))
|
||||||
|
if !ok {
|
||||||
|
return false, f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, f
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckResultPermissions(c *gin.Context, operation CRUD, resultIDSource string, resultIDBody int) (bool, Result) {
|
||||||
|
|
||||||
|
var result Result
|
||||||
|
|
||||||
|
err := ValidateRole(c, ModelResult, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation failed): %v", err.Error()))
|
||||||
|
return false, result
|
||||||
|
}
|
||||||
|
|
||||||
|
resultID, err := helper.GetIDOfElement(c, "resultID", resultIDSource, resultIDBody)
|
||||||
|
if err != nil {
|
||||||
|
return false, result
|
||||||
|
}
|
||||||
|
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&result, uint(resultID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, result
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, _ := CheckScenarioPermissions(c, operation, "body", int(result.ScenarioID))
|
||||||
|
if !ok {
|
||||||
|
return false, result
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, result
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckICPermissions(c *gin.Context, modeltype ModelName, operation CRUD, hasID bool) (bool, InfrastructureComponent) {
|
||||||
|
|
||||||
|
var s InfrastructureComponent
|
||||||
|
|
||||||
|
err := ValidateRole(c, modeltype, operation)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of infrastructure component failed): %v", err.Error()))
|
||||||
|
return false, s
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasID {
|
||||||
|
// Get the ID of the infrastructure component from the context
|
||||||
|
ICID, err := helper.GetIDOfElement(c, "ICID", "path", -1)
|
||||||
|
if err != nil {
|
||||||
|
return false, s
|
||||||
|
}
|
||||||
|
db := GetDB()
|
||||||
|
err = db.Find(&s, uint(ICID)).Error
|
||||||
|
if helper.DBError(c, err) {
|
||||||
|
return false, s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, s
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
308
helper/amqp.go
308
helper/amqp.go
|
@ -1,308 +0,0 @@
|
||||||
/** helper package, AMQP client.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2021, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
|
|
||||||
package helper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/streadway/amqp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AMQPclient struct {
|
|
||||||
connection *amqp.Connection
|
|
||||||
sendCh *amqp.Channel
|
|
||||||
recvCh *amqp.Channel
|
|
||||||
}
|
|
||||||
|
|
||||||
type Action struct {
|
|
||||||
Act string `json:"action"`
|
|
||||||
When int64 `json:"when"`
|
|
||||||
Parameters json.RawMessage `json:"parameters,omitempty"`
|
|
||||||
Model json.RawMessage `json:"model,omitempty"`
|
|
||||||
Results json.RawMessage `json:"results,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Container struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Image string `json:"image"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TemplateSpec struct {
|
|
||||||
Containers []Container `json:"containers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobTemplate struct {
|
|
||||||
Spec TemplateSpec `json:"spec"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobSpec struct {
|
|
||||||
Active string `json:"activeDeadlineSeconds"`
|
|
||||||
Template JobTemplate `json:"template"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobMetaData struct {
|
|
||||||
JobName string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type KubernetesJob struct {
|
|
||||||
Spec JobSpec `json:"spec"`
|
|
||||||
MetaData JobMetaData `json:"metadata"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ICPropertiesToCopy struct {
|
|
||||||
Job KubernetesJob `json:"job"`
|
|
||||||
UUID string `json:"uuid"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Location string `json:"location"`
|
|
||||||
Owner string `json:"owner"`
|
|
||||||
Category string `json:"category"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ICUpdateToCopy struct {
|
|
||||||
Properties ICPropertiesToCopy `json:"properties"`
|
|
||||||
Status json.RawMessage `json:"status"`
|
|
||||||
Schema json.RawMessage `json:"schema"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var client AMQPclient
|
|
||||||
|
|
||||||
const VILLAS_EXCHANGE = "villas"
|
|
||||||
|
|
||||||
type callback func(amqp.Delivery) error
|
|
||||||
|
|
||||||
func ConnectAMQP(uri string, cb callback) error {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// connect to broker
|
|
||||||
client.connection, err = amqp.Dial(uri)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AMQP: failed to connect to RabbitMQ broker %v, error: %v", uri, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create sendCh
|
|
||||||
client.sendCh, err = client.connection.Channel()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AMQP: failed to open a sendCh, error: %v", err)
|
|
||||||
}
|
|
||||||
// declare exchange
|
|
||||||
err = client.sendCh.ExchangeDeclare(VILLAS_EXCHANGE,
|
|
||||||
"headers",
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AMQP: failed to declare the exchange, error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add a queue for the ICs
|
|
||||||
ICQueue, err := client.sendCh.QueueDeclare("",
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
amqp.Table{
|
|
||||||
"x-max-length-bytes": int32(32 << 20),
|
|
||||||
"x-message-ttl": int32(10 * 60),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AMQP: failed to declare the queue, error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = client.sendCh.QueueBind(ICQueue.Name, "", VILLAS_EXCHANGE, false, nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AMQP: failed to bind the queue, error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create receive channel
|
|
||||||
client.recvCh, err = client.connection.Channel()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AMQP: failed to open a recvCh, error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// start deliveries
|
|
||||||
messages, err := client.recvCh.Consume(ICQueue.Name,
|
|
||||||
"",
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AMQP: failed to start deliveries: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// consume deliveries
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
for message := range messages {
|
|
||||||
err = cb(message)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("AMQP: Error processing message: ", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Printf(" AMQP: Waiting for messages... ")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendActionAMQP(action Action, destinationUUID string) error {
|
|
||||||
|
|
||||||
payload, err := json.Marshal(action)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := amqp.Publishing{
|
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
}
|
|
||||||
|
|
||||||
// set message headers
|
|
||||||
msg.Headers = make(map[string]interface{}) // empty map
|
|
||||||
msg.Headers["uuid"] = destinationUUID
|
|
||||||
|
|
||||||
err = CheckConnection()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
//log.Println("AMQP: Sending message", string(msg.Body))
|
|
||||||
err = client.sendCh.Publish(VILLAS_EXCHANGE,
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
msg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return PublishAMQP(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PublishAMQP(msg amqp.Publishing) error {
|
|
||||||
err := client.sendCh.Publish(VILLAS_EXCHANGE,
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
msg)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendPing(uuid string) error {
|
|
||||||
var ping Action
|
|
||||||
ping.Act = "ping"
|
|
||||||
|
|
||||||
payload, err := json.Marshal(ping)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := amqp.Publishing{
|
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
}
|
|
||||||
|
|
||||||
// set message headers
|
|
||||||
msg.Headers = make(map[string]interface{}) // empty map
|
|
||||||
msg.Headers["uuid"] = uuid // leave uuid empty if ping should go to all ICs
|
|
||||||
|
|
||||||
err = CheckConnection()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = client.sendCh.Publish(VILLAS_EXCHANGE,
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
msg)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckConnection() error {
|
|
||||||
|
|
||||||
if client.connection != nil {
|
|
||||||
if client.connection.IsClosed() {
|
|
||||||
return fmt.Errorf("connection to broker is closed")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("connection is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WARNING: this only works with the kubernetes-simple manager of VILLAScontroller
|
|
||||||
func RequestICcreateAMQPsimpleManager(ic *database.InfrastructureComponent, managerUUID string, userName string) (string, error) {
|
|
||||||
newUUID := uuid.New().String()
|
|
||||||
log.Printf("New IC UUID: %s", newUUID)
|
|
||||||
|
|
||||||
var lastUpdate ICUpdateToCopy
|
|
||||||
log.Println(ic.StatusUpdateRaw.RawMessage)
|
|
||||||
err := json.Unmarshal(ic.StatusUpdateRaw.RawMessage, &lastUpdate)
|
|
||||||
if err != nil {
|
|
||||||
return newUUID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := `{"name": "` + lastUpdate.Properties.Name + ` ` + userName + `",` +
|
|
||||||
`"location": "` + lastUpdate.Properties.Location + `",` +
|
|
||||||
`"category": "` + lastUpdate.Properties.Category + `",` +
|
|
||||||
`"type": "` + lastUpdate.Properties.Type + `",` +
|
|
||||||
`"uuid": "` + newUUID + `",` +
|
|
||||||
`"jobname": "` + lastUpdate.Properties.Job.MetaData.JobName + `-` + userName + `",` +
|
|
||||||
`"activeDeadlineSeconds": "` + lastUpdate.Properties.Job.Spec.Active + `",` +
|
|
||||||
`"containername": "` + lastUpdate.Properties.Job.Spec.Template.Spec.Containers[0].Name + `-` + userName + `",` +
|
|
||||||
`"image": "` + lastUpdate.Properties.Job.Spec.Template.Spec.Containers[0].Image + `",` +
|
|
||||||
`"uuid": "` + newUUID + `"}`
|
|
||||||
|
|
||||||
actionCreate := Action{
|
|
||||||
Act: "create",
|
|
||||||
When: time.Now().Unix(),
|
|
||||||
Parameters: json.RawMessage(msg),
|
|
||||||
}
|
|
||||||
|
|
||||||
err = SendActionAMQP(actionCreate, managerUUID)
|
|
||||||
|
|
||||||
return newUUID, err
|
|
||||||
}
|
|
251
helper/amqp_session.go
Normal file
251
helper/amqp_session.go
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/streadway/amqp"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AMQPsession struct {
|
||||||
|
connection *amqp.Connection
|
||||||
|
sendCh *amqp.Channel
|
||||||
|
recvCh *amqp.Channel
|
||||||
|
notifyConnClose chan *amqp.Error
|
||||||
|
notifySendChanClose chan *amqp.Error
|
||||||
|
notifySendConfirm chan amqp.Confirmation
|
||||||
|
notifyRecvChanClose chan *amqp.Error
|
||||||
|
notifyRecvConfirm chan amqp.Confirmation
|
||||||
|
IsReady bool
|
||||||
|
done chan bool
|
||||||
|
name string
|
||||||
|
exchange string
|
||||||
|
processMessage func(delivery amqp.Delivery)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// When reconnecting to the server after connection failure
|
||||||
|
reconnectDelay = 5 * time.Second
|
||||||
|
|
||||||
|
// When setting up the channel after a channel exception
|
||||||
|
reInitDelay = 2 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
//var client AMQPsession
|
||||||
|
|
||||||
|
// NewAMQPSession creates a new consumer state instance, and automatically
|
||||||
|
// attempts to connect to the server.
|
||||||
|
func NewAMQPSession(name string, AMQPurl string, exchange string, processMessage func(delivery amqp.Delivery)) *AMQPsession {
|
||||||
|
if AMQPurl != "" {
|
||||||
|
log.Println("Starting AMQP client")
|
||||||
|
|
||||||
|
session := AMQPsession{
|
||||||
|
name: name,
|
||||||
|
exchange: exchange,
|
||||||
|
done: make(chan bool),
|
||||||
|
processMessage: processMessage,
|
||||||
|
}
|
||||||
|
go session.handleReconnect(AMQPurl)
|
||||||
|
|
||||||
|
return &session
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleReconnect will wait for a connection error on
|
||||||
|
// notifyConnClose, and then continuously attempt to reconnect.
|
||||||
|
func (session *AMQPsession) handleReconnect(addr string) {
|
||||||
|
for {
|
||||||
|
session.IsReady = false
|
||||||
|
log.Println("Attempting to connect to AMQP broker ", addr)
|
||||||
|
|
||||||
|
conn, err := session.connect(addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to connect. Retrying...")
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.done:
|
||||||
|
return
|
||||||
|
case <-time.After(reconnectDelay):
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if done := session.handleReInit(conn); done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect will create a new AMQP connection
|
||||||
|
func (session *AMQPsession) connect(addr string) (*amqp.Connection, error) {
|
||||||
|
conn, err := amqp.Dial(addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// take a new connection to the queue, and updates the close listener to reflect this.
|
||||||
|
session.connection = conn
|
||||||
|
session.notifyConnClose = make(chan *amqp.Error)
|
||||||
|
session.connection.NotifyClose(session.notifyConnClose)
|
||||||
|
|
||||||
|
log.Println("Connected!")
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleReInit will wait for a channel error
|
||||||
|
// and then continuously attempt to re-initialize both channels
|
||||||
|
func (session *AMQPsession) handleReInit(conn *amqp.Connection) bool {
|
||||||
|
for {
|
||||||
|
session.IsReady = false
|
||||||
|
|
||||||
|
err := session.init(conn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to initialize channel. Retrying...")
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.done:
|
||||||
|
return true
|
||||||
|
case <-time.After(reInitDelay):
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.done:
|
||||||
|
return true
|
||||||
|
case <-session.notifyConnClose:
|
||||||
|
log.Println("Connection closed. Reconnecting...")
|
||||||
|
return false
|
||||||
|
case <-session.notifySendChanClose:
|
||||||
|
log.Println("Send channel closed. Re-running init...")
|
||||||
|
case <-session.notifyRecvChanClose:
|
||||||
|
log.Println("Receive channel closed. Re-running init...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// init will initialize channel & declare queue
|
||||||
|
func (session *AMQPsession) init(conn *amqp.Connection) error {
|
||||||
|
|
||||||
|
// create sendCh
|
||||||
|
sendCh, err := conn.Channel()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("AMQP: failed to open a sendCh, error: %v", err)
|
||||||
|
}
|
||||||
|
// declare exchange
|
||||||
|
err = sendCh.ExchangeDeclare(session.exchange,
|
||||||
|
"headers",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("AMQP: failed to declare the exchange, error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a queue for the ICs
|
||||||
|
ICQueue, err := sendCh.QueueDeclare("",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("AMQP: failed to declare the queue, error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sendCh.QueueBind(ICQueue.Name, "", session.exchange, false, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("AMQP: failed to bind the queue, error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
session.sendCh = sendCh
|
||||||
|
session.notifySendChanClose = make(chan *amqp.Error)
|
||||||
|
session.notifySendConfirm = make(chan amqp.Confirmation, 1)
|
||||||
|
session.sendCh.NotifyClose(session.notifySendChanClose)
|
||||||
|
session.sendCh.NotifyPublish(session.notifySendConfirm)
|
||||||
|
|
||||||
|
// create receive channel
|
||||||
|
recvCh, err := conn.Channel()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("AMQP: failed to open a recvCh, error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
session.recvCh = recvCh
|
||||||
|
session.notifyRecvChanClose = make(chan *amqp.Error)
|
||||||
|
session.notifyRecvConfirm = make(chan amqp.Confirmation, 1)
|
||||||
|
session.recvCh.NotifyClose(session.notifyRecvChanClose)
|
||||||
|
session.recvCh.NotifyPublish(session.notifyRecvConfirm)
|
||||||
|
|
||||||
|
// start deliveries
|
||||||
|
messages, err := session.recvCh.Consume(ICQueue.Name,
|
||||||
|
"",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("AMQP: failed to start deliveries: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume deliveries
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
for message := range messages {
|
||||||
|
session.processMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
session.IsReady = true
|
||||||
|
log.Println("AMQP channels setup! Waiting for messages...")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session *AMQPsession) CheckConnection() error {
|
||||||
|
|
||||||
|
if session.connection != nil {
|
||||||
|
if session.connection.IsClosed() {
|
||||||
|
return fmt.Errorf("connection to broker is closed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("connection is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session *AMQPsession) Send(payload []byte, destinationUuid string) error {
|
||||||
|
|
||||||
|
message := amqp.Publishing{
|
||||||
|
DeliveryMode: 2,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
ContentType: "application/json",
|
||||||
|
ContentEncoding: "utf-8",
|
||||||
|
Priority: 0,
|
||||||
|
Body: payload,
|
||||||
|
}
|
||||||
|
|
||||||
|
// set message headers
|
||||||
|
message.Headers = make(map[string]interface{}) // empty map
|
||||||
|
message.Headers["uuid"] = destinationUuid // leave uuid empty if message should go to all ICs
|
||||||
|
|
||||||
|
err := session.CheckConnection()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.sendCh.Publish(session.exchange,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
message)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -29,10 +29,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/nsf/jsondiff"
|
"github.com/nsf/jsondiff"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// data type used in testing
|
// data type used in testing
|
||||||
|
@ -42,28 +40,6 @@ type KeyModels map[string]interface{}
|
||||||
// #################### User data used for testing #######################
|
// #################### User data used for testing #######################
|
||||||
// #######################################################################
|
// #######################################################################
|
||||||
|
|
||||||
// Credentials
|
|
||||||
var StrPassword0 = "xyz789"
|
|
||||||
var StrPasswordA = "abc123"
|
|
||||||
var StrPasswordB = "bcd234"
|
|
||||||
var StrPasswordC = "guestpw"
|
|
||||||
|
|
||||||
// Hash passwords with bcrypt algorithm
|
|
||||||
var bcryptCost = 10
|
|
||||||
var pw0, _ = bcrypt.GenerateFromPassword([]byte(StrPassword0), bcryptCost)
|
|
||||||
var pwA, _ = bcrypt.GenerateFromPassword([]byte(StrPasswordA), bcryptCost)
|
|
||||||
var pwB, _ = bcrypt.GenerateFromPassword([]byte(StrPasswordB), bcryptCost)
|
|
||||||
var pwC, _ = bcrypt.GenerateFromPassword([]byte(StrPasswordC), bcryptCost)
|
|
||||||
|
|
||||||
var User0 = database.User{Username: "User_0", Password: string(pw0),
|
|
||||||
Role: "Admin", Mail: "User_0@example.com"}
|
|
||||||
var UserA = database.User{Username: "User_A", Password: string(pwA),
|
|
||||||
Role: "User", Mail: "User_A@example.com", Active: true}
|
|
||||||
var UserB = database.User{Username: "User_B", Password: string(pwB),
|
|
||||||
Role: "User", Mail: "User_B@example.com", Active: true}
|
|
||||||
var UserC = database.User{Username: "User_C", Password: string(pwC),
|
|
||||||
Role: "Guest", Mail: "User_C@example.com", Active: true}
|
|
||||||
|
|
||||||
type UserRequest struct {
|
type UserRequest struct {
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
@ -73,31 +49,6 @@ type UserRequest struct {
|
||||||
Active string `json:"active,omitempty"`
|
Active string `json:"active,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Credentials struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var AdminCredentials = Credentials{
|
|
||||||
Username: User0.Username,
|
|
||||||
Password: StrPassword0,
|
|
||||||
}
|
|
||||||
|
|
||||||
var UserACredentials = Credentials{
|
|
||||||
Username: UserA.Username,
|
|
||||||
Password: StrPasswordA,
|
|
||||||
}
|
|
||||||
|
|
||||||
var UserBCredentials = Credentials{
|
|
||||||
Username: UserB.Username,
|
|
||||||
Password: StrPasswordB,
|
|
||||||
}
|
|
||||||
|
|
||||||
var GuestCredentials = Credentials{
|
|
||||||
Username: UserC.Username,
|
|
||||||
Password: StrPasswordC,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ############################################################################
|
// ############################################################################
|
||||||
// #################### Functions used for testing ############################
|
// #################### Functions used for testing ############################
|
||||||
// ############################################################################
|
// ############################################################################
|
||||||
|
@ -315,20 +266,3 @@ func AuthenticateForTest(router *gin.Engine, credentials interface{}) (string, e
|
||||||
// Return the token and nil error
|
// Return the token and nil error
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// add test users defined above
|
|
||||||
func AddTestUsers() error {
|
|
||||||
|
|
||||||
testUsers := []database.User{User0, UserA, UserB, UserC}
|
|
||||||
database.DBpool.AutoMigrate(&database.User{})
|
|
||||||
|
|
||||||
for _, user := range testUsers {
|
|
||||||
err := database.DBpool.Create(&user).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterComponentConfigurationEndpoints(r *gin.RouterGroup) {
|
func RegisterComponentConfigurationEndpoints(r *gin.RouterGroup) {
|
||||||
|
@ -54,7 +53,7 @@ func RegisterComponentConfigurationEndpoints(r *gin.RouterGroup) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getConfigs(c *gin.Context) {
|
func getConfigs(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := scenario.CheckPermissions(c, database.Read, "query", -1)
|
ok, so := database.CheckScenarioPermissions(c, database.Read, "query", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -102,7 +101,7 @@ func addConfig(c *gin.Context) {
|
||||||
newConfig := req.createConfig()
|
newConfig := req.createConfig()
|
||||||
|
|
||||||
// check access to the scenario
|
// check access to the scenario
|
||||||
ok, _ := scenario.CheckPermissions(c, database.Update, "body", int(newConfig.ScenarioID))
|
ok, _ := database.CheckScenarioPermissions(c, database.Update, "body", int(newConfig.ScenarioID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -132,11 +131,15 @@ func addConfig(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func updateConfig(c *gin.Context) {
|
func updateConfig(c *gin.Context) {
|
||||||
|
|
||||||
ok, oldConfig := CheckPermissions(c, database.Update, "path", -1)
|
ok, oldConfig_r := database.CheckComponentConfigPermissions(c, database.Update, "path", -1)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldConfig ComponentConfiguration
|
||||||
|
oldConfig.ComponentConfiguration = oldConfig_r
|
||||||
|
|
||||||
var req updateConfigRequest
|
var req updateConfigRequest
|
||||||
err := c.BindJSON(&req)
|
err := c.BindJSON(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -176,12 +179,12 @@ func updateConfig(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getConfig(c *gin.Context) {
|
func getConfig(c *gin.Context) {
|
||||||
|
|
||||||
ok, m := CheckPermissions(c, database.Read, "path", -1)
|
ok, m := database.CheckComponentConfigPermissions(c, database.Read, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"config": m.ComponentConfiguration})
|
c.JSON(http.StatusOK, gin.H{"config": m})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteConfig godoc
|
// deleteConfig godoc
|
||||||
|
@ -199,11 +202,14 @@ func getConfig(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteConfig(c *gin.Context) {
|
func deleteConfig(c *gin.Context) {
|
||||||
|
|
||||||
ok, m := CheckPermissions(c, database.Delete, "path", -1)
|
ok, m_r := database.CheckComponentConfigPermissions(c, database.Delete, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var m ComponentConfiguration
|
||||||
|
m.ComponentConfiguration = m_r
|
||||||
|
|
||||||
err := m.delete()
|
err := m.delete()
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
c.JSON(http.StatusOK, gin.H{"config": m.ComponentConfiguration})
|
c.JSON(http.StatusOK, gin.H{"config": m.ComponentConfiguration})
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ComponentConfiguration struct {
|
type ComponentConfiguration struct {
|
||||||
|
@ -49,8 +48,8 @@ func (m *ComponentConfiguration) ByID(id uint) error {
|
||||||
|
|
||||||
func (m *ComponentConfiguration) addToScenario() error {
|
func (m *ComponentConfiguration) addToScenario() error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var so scenario.Scenario
|
var so database.Scenario
|
||||||
err := so.ByID(m.ScenarioID)
|
err := db.Find(&so, m.ScenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -120,8 +119,8 @@ func (m *ComponentConfiguration) Update(modifiedConfig ComponentConfiguration) e
|
||||||
func (m *ComponentConfiguration) delete() error {
|
func (m *ComponentConfiguration) delete() error {
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var so scenario.Scenario
|
var so database.Scenario
|
||||||
err := so.ByID(m.ScenarioID)
|
err := db.Find(&so, m.ScenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
/** component_configuration package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
package component_configuration
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckPermissions(c *gin.Context, operation database.CRUD, configIDSource string, configIDBody int) (bool, ComponentConfiguration) {
|
|
||||||
|
|
||||||
var m ComponentConfiguration
|
|
||||||
|
|
||||||
err := database.ValidateRole(c, database.ModelComponentConfiguration, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of Component Configuration failed): %v", err.Error()))
|
|
||||||
return false, m
|
|
||||||
}
|
|
||||||
|
|
||||||
configID, err := helper.GetIDOfElement(c, "configID", configIDSource, configIDBody)
|
|
||||||
if err != nil {
|
|
||||||
return false, m
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.ByID(uint(configID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, m
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, _ := scenario.CheckPermissions(c, operation, "body", int(m.ScenarioID))
|
|
||||||
if !ok {
|
|
||||||
return false, m
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, m
|
|
||||||
}
|
|
|
@ -81,7 +81,7 @@ func newFalse() *bool {
|
||||||
func addScenarioAndIC() (scenarioID uint, ICID uint) {
|
func addScenarioAndIC() (scenarioID uint, ICID uint) {
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, _ := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, _ := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
|
|
||||||
// POST $newICA
|
// POST $newICA
|
||||||
newICA := ICRequest{
|
newICA := ICRequest{
|
||||||
|
@ -116,7 +116,7 @@ func addScenarioAndIC() (scenarioID uint, ICID uint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, _ = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, _ = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
|
|
||||||
// POST $newScenario
|
// POST $newScenario
|
||||||
newScenario := ScenarioRequest{
|
newScenario := ScenarioRequest{
|
||||||
|
@ -172,7 +172,7 @@ func TestMain(m *testing.M) {
|
||||||
func TestAddConfig(t *testing.T) {
|
func TestAddConfig(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -182,7 +182,7 @@ func TestAddConfig(t *testing.T) {
|
||||||
newConfig1.ScenarioID = scenarioID
|
newConfig1.ScenarioID = scenarioID
|
||||||
newConfig1.ICID = ICID
|
newConfig1.ICID = ICID
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST with no access
|
// try to POST with no access
|
||||||
|
@ -193,7 +193,7 @@ func TestAddConfig(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST non JSON body
|
// try to POST non JSON body
|
||||||
|
@ -203,7 +203,7 @@ func TestAddConfig(t *testing.T) {
|
||||||
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST newConfig
|
// test POST newConfig
|
||||||
|
@ -242,7 +242,7 @@ func TestAddConfig(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Try to GET the newConfig with no access
|
// Try to GET the newConfig with no access
|
||||||
|
@ -258,7 +258,7 @@ func TestUpdateConfig(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -266,7 +266,7 @@ func TestUpdateConfig(t *testing.T) {
|
||||||
scenarioID, ICID := addScenarioAndIC()
|
scenarioID, ICID := addScenarioAndIC()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST newConfig
|
// test POST newConfig
|
||||||
|
@ -287,7 +287,7 @@ func TestUpdateConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT with no access
|
// try to PUT with no access
|
||||||
|
@ -298,7 +298,7 @@ func TestUpdateConfig(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user who has access to component config
|
// authenticate as guest user who has access to component config
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT as guest
|
// try to PUT as guest
|
||||||
|
@ -309,7 +309,7 @@ func TestUpdateConfig(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT a non JSON body
|
// try to PUT a non JSON body
|
||||||
|
@ -361,7 +361,7 @@ func TestUpdateConfig(t *testing.T) {
|
||||||
func TestDeleteConfig(t *testing.T) {
|
func TestDeleteConfig(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -371,7 +371,7 @@ func TestDeleteConfig(t *testing.T) {
|
||||||
newConfig1.ICID = ICID
|
newConfig1.ICID = ICID
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST newConfig
|
// test POST newConfig
|
||||||
|
@ -385,7 +385,7 @@ func TestDeleteConfig(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to DELETE with no access
|
// try to DELETE with no access
|
||||||
|
@ -396,7 +396,7 @@ func TestDeleteConfig(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the component config returned for scenario
|
// Count the number of all the component config returned for scenario
|
||||||
|
@ -425,7 +425,7 @@ func TestDeleteConfig(t *testing.T) {
|
||||||
func TestGetAllConfigsOfScenario(t *testing.T) {
|
func TestGetAllConfigsOfScenario(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -435,7 +435,7 @@ func TestGetAllConfigsOfScenario(t *testing.T) {
|
||||||
newConfig1.ICID = ICID
|
newConfig1.ICID = ICID
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST newConfig
|
// test POST newConfig
|
||||||
|
@ -452,7 +452,7 @@ func TestGetAllConfigsOfScenario(t *testing.T) {
|
||||||
assert.Equal(t, 1, NumberOfConfigs)
|
assert.Equal(t, 1, NumberOfConfigs)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to scenario
|
// authenticate as normal userB who has no access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to get configs without access
|
// try to get configs without access
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterDashboardEndpoints(r *gin.RouterGroup) {
|
func RegisterDashboardEndpoints(r *gin.RouterGroup) {
|
||||||
|
@ -55,7 +54,7 @@ func RegisterDashboardEndpoints(r *gin.RouterGroup) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getDashboards(c *gin.Context) {
|
func getDashboards(c *gin.Context) {
|
||||||
|
|
||||||
ok, sim := scenario.CheckPermissions(c, database.Read, "query", -1)
|
ok, sim := database.CheckScenarioPermissions(c, database.Read, "query", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -102,7 +101,7 @@ func addDashboard(c *gin.Context) {
|
||||||
newDashboard := req.createDashboard()
|
newDashboard := req.createDashboard()
|
||||||
|
|
||||||
// Check if user is allowed to modify scenario specified in request
|
// Check if user is allowed to modify scenario specified in request
|
||||||
ok, _ := scenario.CheckPermissions(c, database.Update, "body", int(newDashboard.ScenarioID))
|
ok, _ := database.CheckScenarioPermissions(c, database.Update, "body", int(newDashboard.ScenarioID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -132,11 +131,14 @@ func addDashboard(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func updateDashboard(c *gin.Context) {
|
func updateDashboard(c *gin.Context) {
|
||||||
|
|
||||||
ok, oldDashboard := CheckPermissions(c, database.Update, "path", -1)
|
ok, oldDashboard_r := database.CheckDashboardPermissions(c, database.Update, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldDashboard Dashboard
|
||||||
|
oldDashboard.Dashboard = oldDashboard_r
|
||||||
|
|
||||||
var req updateDashboardRequest
|
var req updateDashboardRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.BadRequestError(c, err.Error())
|
helper.BadRequestError(c, err.Error())
|
||||||
|
@ -174,12 +176,12 @@ func updateDashboard(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getDashboard(c *gin.Context) {
|
func getDashboard(c *gin.Context) {
|
||||||
|
|
||||||
ok, dab := CheckPermissions(c, database.Read, "path", -1)
|
ok, dab := database.CheckDashboardPermissions(c, database.Read, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"dashboard": dab.Dashboard})
|
c.JSON(http.StatusOK, gin.H{"dashboard": dab})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteDashboard godoc
|
// deleteDashboard godoc
|
||||||
|
@ -196,11 +198,14 @@ func getDashboard(c *gin.Context) {
|
||||||
// @Router /dashboards/{dashboardID} [delete]
|
// @Router /dashboards/{dashboardID} [delete]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteDashboard(c *gin.Context) {
|
func deleteDashboard(c *gin.Context) {
|
||||||
ok, dab := CheckPermissions(c, database.Delete, "path", -1)
|
ok, dab_r := database.CheckDashboardPermissions(c, database.Delete, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dab Dashboard
|
||||||
|
dab.Dashboard = dab_r
|
||||||
|
|
||||||
err := dab.delete()
|
err := dab.delete()
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
c.JSON(http.StatusOK, gin.H{"dashboard": dab.Dashboard})
|
c.JSON(http.StatusOK, gin.H{"dashboard": dab.Dashboard})
|
||||||
|
|
|
@ -23,7 +23,6 @@ package dashboard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Dashboard struct {
|
type Dashboard struct {
|
||||||
|
@ -47,8 +46,8 @@ func (d *Dashboard) ByID(id uint) error {
|
||||||
|
|
||||||
func (d *Dashboard) addToScenario() error {
|
func (d *Dashboard) addToScenario() error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var sim scenario.Scenario
|
var sim database.Scenario
|
||||||
err := sim.ByID(d.ScenarioID)
|
err := db.Find(&sim, d.ScenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -81,8 +80,8 @@ func (d *Dashboard) update(modifiedDab Dashboard) error {
|
||||||
func (d *Dashboard) delete() error {
|
func (d *Dashboard) delete() error {
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var sim scenario.Scenario
|
var sim database.Scenario
|
||||||
err := sim.ByID(d.ScenarioID)
|
err := db.Find(&sim, d.ScenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
/** Dashboard package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
package dashboard
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckPermissions(c *gin.Context, operation database.CRUD, dabIDSource string, dabIDBody int) (bool, Dashboard) {
|
|
||||||
|
|
||||||
var dab Dashboard
|
|
||||||
|
|
||||||
err := database.ValidateRole(c, database.ModelDashboard, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation failed): %v", err.Error()))
|
|
||||||
return false, dab
|
|
||||||
}
|
|
||||||
|
|
||||||
dabID, err := helper.GetIDOfElement(c, "dashboardID", dabIDSource, dabIDBody)
|
|
||||||
if err != nil {
|
|
||||||
return false, dab
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dab.ByID(uint(dabID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, dab
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, _ := scenario.CheckPermissions(c, operation, "body", int(dab.ScenarioID))
|
|
||||||
if !ok {
|
|
||||||
return false, dab
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, dab
|
|
||||||
}
|
|
|
@ -109,10 +109,10 @@ func TestMain(m *testing.M) {
|
||||||
func TestAddDashboard(t *testing.T) {
|
func TestAddDashboard(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
scenarioID := addScenario(token)
|
scenarioID := addScenario(token)
|
||||||
|
@ -168,7 +168,7 @@ func TestAddDashboard(t *testing.T) {
|
||||||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// try to get dashboard as a user that is not in the scenario (userB)
|
// try to get dashboard as a user that is not in the scenario (userB)
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// this should fail with unprocessable entity
|
// this should fail with unprocessable entity
|
||||||
|
@ -189,10 +189,10 @@ func TestAddDashboard(t *testing.T) {
|
||||||
func TestUpdateDashboard(t *testing.T) {
|
func TestUpdateDashboard(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
scenarioID := addScenario(token)
|
scenarioID := addScenario(token)
|
||||||
|
@ -214,7 +214,7 @@ func TestUpdateDashboard(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate as guest user
|
// authenticate as guest user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to update a dashboard as guest
|
// try to update a dashboard as guest
|
||||||
|
@ -225,7 +225,7 @@ func TestUpdateDashboard(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
code, resp, err = helper.TestEndpoint(router, token,
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
|
@ -263,10 +263,10 @@ func TestUpdateDashboard(t *testing.T) {
|
||||||
func TestDeleteDashboard(t *testing.T) {
|
func TestDeleteDashboard(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
scenarioID := addScenario(token)
|
scenarioID := addScenario(token)
|
||||||
|
@ -283,7 +283,7 @@ func TestDeleteDashboard(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to delete a dashboard from a scenario to which the user has no access
|
// try to delete a dashboard from a scenario to which the user has no access
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// this should fail with unprocessable entity
|
// this should fail with unprocessable entity
|
||||||
|
@ -293,7 +293,7 @@ func TestDeleteDashboard(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to delete a dashboard that does not exist; should return a not found error
|
// try to delete a dashboard that does not exist; should return a not found error
|
||||||
|
@ -329,10 +329,10 @@ func TestDeleteDashboard(t *testing.T) {
|
||||||
func TestGetAllDashboardsOfScenario(t *testing.T) {
|
func TestGetAllDashboardsOfScenario(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
scenarioID := addScenario(token)
|
scenarioID := addScenario(token)
|
||||||
|
@ -374,7 +374,7 @@ func TestGetAllDashboardsOfScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// try to get all dashboards as a user that does not belong to scenario
|
// try to get all dashboards as a user that does not belong to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// this should fail with unprocessable entity
|
// this should fail with unprocessable entity
|
||||||
|
|
|
@ -26,7 +26,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
|
@ -54,7 +53,7 @@ func RegisterFileEndpoints(r *gin.RouterGroup) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getFiles(c *gin.Context) {
|
func getFiles(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := scenario.CheckPermissions(c, database.Read, "query", -1)
|
ok, so := database.CheckScenarioPermissions(c, database.Read, "query", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -92,7 +91,7 @@ func getFiles(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func addFile(c *gin.Context) {
|
func addFile(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := scenario.CheckPermissions(c, database.Read, "query", -1)
|
ok, so := database.CheckScenarioPermissions(c, database.Read, "query", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -142,11 +141,14 @@ func addFile(c *gin.Context) {
|
||||||
func getFile(c *gin.Context) {
|
func getFile(c *gin.Context) {
|
||||||
|
|
||||||
// check access
|
// check access
|
||||||
ok, f := CheckPermissions(c, database.Read)
|
ok, f_r := database.CheckFilePermissions(c, database.Read)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var f File
|
||||||
|
f.File = f_r
|
||||||
|
|
||||||
err := f.download(c)
|
err := f.download(c)
|
||||||
helper.DBError(c, err)
|
helper.DBError(c, err)
|
||||||
}
|
}
|
||||||
|
@ -175,11 +177,14 @@ func getFile(c *gin.Context) {
|
||||||
func updateFile(c *gin.Context) {
|
func updateFile(c *gin.Context) {
|
||||||
|
|
||||||
// check access
|
// check access
|
||||||
ok, f := CheckPermissions(c, database.Update)
|
ok, f_r := database.CheckFilePermissions(c, database.Update)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var f File
|
||||||
|
f.File = f_r
|
||||||
|
|
||||||
// Extract file from PUT request form
|
// Extract file from PUT request form
|
||||||
fileHeader, err := c.FormFile("file")
|
fileHeader, err := c.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -209,11 +214,14 @@ func updateFile(c *gin.Context) {
|
||||||
func deleteFile(c *gin.Context) {
|
func deleteFile(c *gin.Context) {
|
||||||
|
|
||||||
// check access
|
// check access
|
||||||
ok, f := CheckPermissions(c, database.Delete)
|
ok, f_r := database.CheckFilePermissions(c, database.Delete)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var f File
|
||||||
|
f.File = f_r
|
||||||
|
|
||||||
err := f.Delete()
|
err := f.Delete()
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
c.JSON(http.StatusOK, gin.H{"file": f.File})
|
c.JSON(http.StatusOK, gin.H{"file": f.File})
|
||||||
|
|
|
@ -36,7 +36,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
|
@ -145,8 +144,8 @@ func (f *File) Register(fileHeader *multipart.FileHeader, scenarioID uint) error
|
||||||
// Create association to scenario
|
// Create association to scenario
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
|
|
||||||
var so scenario.Scenario
|
var so database.Scenario
|
||||||
err = so.ByID(scenarioID)
|
err = db.Find(&so, scenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -229,8 +228,8 @@ func (f *File) Delete() error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
|
|
||||||
// remove association between file and scenario
|
// remove association between file and scenario
|
||||||
var so scenario.Scenario
|
var so database.Scenario
|
||||||
err := so.ByID(f.ScenarioID)
|
err := db.Find(&so, f.ScenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
/** File package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckPermissions(c *gin.Context, operation database.CRUD) (bool, File) {
|
|
||||||
|
|
||||||
var f File
|
|
||||||
|
|
||||||
err := database.ValidateRole(c, database.ModelFile, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of file failed): %v", err.Error()))
|
|
||||||
return false, f
|
|
||||||
}
|
|
||||||
|
|
||||||
fileID, err := helper.GetIDOfElement(c, "fileID", "path", -1)
|
|
||||||
if err != nil {
|
|
||||||
return false, f
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.ByID(uint(fileID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, f
|
|
||||||
}
|
|
||||||
|
|
||||||
if operation != database.Read {
|
|
||||||
// check access to scenario only if operation is not Read (=download) of file
|
|
||||||
ok, _ := scenario.CheckPermissions(c, operation, "body", int(f.ScenarioID))
|
|
||||||
if !ok {
|
|
||||||
return false, f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, f
|
|
||||||
}
|
|
|
@ -53,10 +53,10 @@ type ScenarioRequest struct {
|
||||||
func addScenario() (scenarioID uint) {
|
func addScenario() (scenarioID uint) {
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
_, _ = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
_, _ = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, _ := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, _ := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
|
|
||||||
// POST $newScenario
|
// POST $newScenario
|
||||||
newScenario := ScenarioRequest{
|
newScenario := ScenarioRequest{
|
||||||
|
@ -103,14 +103,14 @@ func TestMain(m *testing.M) {
|
||||||
func TestAddFile(t *testing.T) {
|
func TestAddFile(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// using the respective endpoints of the API
|
// using the respective endpoints of the API
|
||||||
scenarioID := addScenario()
|
scenarioID := addScenario()
|
||||||
|
|
||||||
// authenticate as userB who has no access to the elements in the DB
|
// authenticate as userB who has no access to the elements in the DB
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
emptyBuf := &bytes.Buffer{}
|
emptyBuf := &bytes.Buffer{}
|
||||||
|
@ -123,7 +123,7 @@ func TestAddFile(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userA
|
// authenticate as normal userA
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST without a scenario ID
|
// try to POST without a scenario ID
|
||||||
|
@ -189,14 +189,14 @@ func TestUpdateFile(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// using the respective endpoints of the API
|
// using the respective endpoints of the API
|
||||||
scenarioID := addScenario()
|
scenarioID := addScenario()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create a testfile.txt in local folder
|
// create a testfile.txt in local folder
|
||||||
|
@ -236,7 +236,7 @@ func TestUpdateFile(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as userB who has no access to the elements in the DB
|
// authenticate as userB who has no access to the elements in the DB
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
emptyBuf := &bytes.Buffer{}
|
emptyBuf := &bytes.Buffer{}
|
||||||
|
@ -249,7 +249,7 @@ func TestUpdateFile(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user C
|
// authenticate as guest user C
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT as guest
|
// try to PUT as guest
|
||||||
|
@ -261,7 +261,7 @@ func TestUpdateFile(t *testing.T) {
|
||||||
|
|
||||||
// Prepare update
|
// Prepare update
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT with empty body
|
// try to PUT with empty body
|
||||||
|
@ -319,14 +319,14 @@ func TestUpdateFile(t *testing.T) {
|
||||||
func TestDeleteFile(t *testing.T) {
|
func TestDeleteFile(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// using the respective endpoints of the API
|
// using the respective endpoints of the API
|
||||||
scenarioID := addScenario()
|
scenarioID := addScenario()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create a testfile.txt in local folder
|
// create a testfile.txt in local folder
|
||||||
|
@ -386,7 +386,7 @@ func TestDeleteFile(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as userB who has no access to the elements in the DB
|
// authenticate as userB who has no access to the elements in the DB
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to DELETE file from scenario to which userB has no access
|
// try to DELETE file from scenario to which userB has no access
|
||||||
|
@ -397,7 +397,7 @@ func TestDeleteFile(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all files returned for scenario
|
// Count the number of all files returned for scenario
|
||||||
|
@ -413,7 +413,7 @@ func TestDeleteFile(t *testing.T) {
|
||||||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user C
|
// authenticate as guest user C
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to DELETE file of scenario as guest
|
// try to DELETE file of scenario as guest
|
||||||
|
@ -424,7 +424,7 @@ func TestDeleteFile(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Delete the added file 1
|
// Delete the added file 1
|
||||||
|
@ -451,14 +451,14 @@ func TestGetAllFilesOfScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// using the respective endpoints of the API
|
// using the respective endpoints of the API
|
||||||
scenarioID := addScenario()
|
scenarioID := addScenario()
|
||||||
|
|
||||||
// authenticate as userB who has no access to the elements in the DB
|
// authenticate as userB who has no access to the elements in the DB
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to get all files for scenario to which userB has not access
|
// try to get all files for scenario to which userB has not access
|
||||||
|
@ -469,7 +469,7 @@ func TestGetAllFilesOfScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userA
|
// authenticate as normal userA
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
//try to get all files with missing scenario ID; should return a bad request error
|
//try to get all files with missing scenario ID; should return a bad request error
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
package healthz
|
package healthz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -31,11 +32,17 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var session *helper.AMQPsession
|
||||||
|
|
||||||
func RegisterHealthzEndpoint(r *gin.RouterGroup) {
|
func RegisterHealthzEndpoint(r *gin.RouterGroup) {
|
||||||
|
|
||||||
r.GET("", getHealth)
|
r.GET("", getHealth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetAMQPSession(s *helper.AMQPsession) {
|
||||||
|
session = s
|
||||||
|
}
|
||||||
|
|
||||||
// getHealth godoc
|
// getHealth godoc
|
||||||
// @Summary Get health status of backend
|
// @Summary Get health status of backend
|
||||||
// @ID getHealth
|
// @ID getHealth
|
||||||
|
@ -67,12 +74,20 @@ func getHealth(c *gin.Context) {
|
||||||
c.Writer.WriteHeader(http.StatusNoContent)
|
c.Writer.WriteHeader(http.StatusNoContent)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
err = helper.CheckConnection()
|
if session != nil {
|
||||||
if err != nil {
|
err = session.CheckConnection()
|
||||||
log.Println(err.Error())
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"success:": false,
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
"success:": false,
|
"success:": false,
|
||||||
"message": err.Error(),
|
"message": fmt.Errorf("AMQP session is nil"),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,8 +80,14 @@ func TestHealthz(t *testing.T) {
|
||||||
amqpURI := "amqp://" + user + ":" + pass + "@" + host
|
amqpURI := "amqp://" + user + ":" + pass + "@" + host
|
||||||
log.Println("AMQP URI is", amqpURI)
|
log.Println("AMQP URI is", amqpURI)
|
||||||
|
|
||||||
err = helper.ConnectAMQP(amqpURI, infrastructure_component.ProcessMessage)
|
session = helper.NewAMQPSession("villas-test-session", amqpURI, "villas", infrastructure_component.ProcessMessage)
|
||||||
assert.NoError(t, err)
|
SetAMQPSession(session)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if session.IsReady {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// test healthz endpoint for connected DB and AMQP client
|
// test healthz endpoint for connected DB and AMQP client
|
||||||
code, resp, err = helper.TestEndpoint(router, "", "/api/v2/healthz", http.MethodGet, nil)
|
code, resp, err = helper.TestEndpoint(router, "", "/api/v2/healthz", http.MethodGet, nil)
|
||||||
|
|
|
@ -26,14 +26,20 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
"github.com/jinzhu/gorm/dialects/postgres"
|
"github.com/jinzhu/gorm/dialects/postgres"
|
||||||
"github.com/streadway/amqp"
|
"github.com/streadway/amqp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Act string `json:"action"`
|
||||||
|
When int64 `json:"when"`
|
||||||
|
Parameters json.RawMessage `json:"parameters,omitempty"`
|
||||||
|
Model json.RawMessage `json:"model,omitempty"`
|
||||||
|
Results json.RawMessage `json:"results,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ICStatus struct {
|
type ICStatus struct {
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
@ -68,42 +74,24 @@ type ICUpdate struct {
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartAMQP(AMQPurl string, api *gin.RouterGroup) error {
|
func ProcessMessage(message amqp.Delivery) {
|
||||||
if AMQPurl != "" {
|
|
||||||
log.Println("Starting AMQP client")
|
|
||||||
|
|
||||||
err := helper.ConnectAMQP(AMQPurl, ProcessMessage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// register IC action endpoint only if AMQP client is used
|
|
||||||
RegisterAMQPEndpoint(api.Group("/ic"))
|
|
||||||
|
|
||||||
log.Printf("Connected AMQP client to %s", AMQPurl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProcessMessage(message amqp.Delivery) error {
|
|
||||||
|
|
||||||
var payload ICUpdate
|
var payload ICUpdate
|
||||||
err := json.Unmarshal(message.Body, &payload)
|
err := json.Unmarshal(message.Body, &payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("AMQP: Could not unmarshal message to JSON: %v err: %v", string(message.Body), err)
|
log.Printf("AMQP: Could not unmarshal message to JSON: %v err: %v", string(message.Body), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if payload.Action != "" {
|
if payload.Action != "" {
|
||||||
// if a message contains an action, it is not intended for the backend
|
// if a message contains an action, it is not intended for the backend
|
||||||
//log.Println("AMQP: Ignoring action message ", payload)
|
//log.Println("AMQP: Ignoring action message ", payload)
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ICUUID := payload.Properties.UUID
|
ICUUID := payload.Properties.UUID
|
||||||
_, err = uuid.Parse(ICUUID)
|
_, err = uuid.Parse(ICUUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("amqp: UUID not valid: %v, message ignored: %vi", ICUUID, string(message.Body))
|
log.Printf("amqp: UUID not valid: %v, message ignored: %v \n", ICUUID, string(message.Body))
|
||||||
}
|
}
|
||||||
|
|
||||||
var sToBeUpdated InfrastructureComponent
|
var sToBeUpdated InfrastructureComponent
|
||||||
|
@ -119,8 +107,10 @@ func ProcessMessage(message amqp.Delivery) error {
|
||||||
// update record based on payload
|
// update record based on payload
|
||||||
err = sToBeUpdated.updateExternalIC(payload, message.Body)
|
err = sToBeUpdated.updateExternalIC(payload, message.Body)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createExternalIC(payload ICUpdate, ICUUID string, body []byte) error {
|
func createExternalIC(payload ICUpdate, ICUUID string, body []byte) error {
|
||||||
|
@ -178,7 +168,7 @@ func createExternalIC(payload ICUpdate, ICUUID string, body []byte) error {
|
||||||
log.Println("AMQP: Created IC with UUID ", newIC.UUID)
|
log.Println("AMQP: Created IC with UUID ", newIC.UUID)
|
||||||
|
|
||||||
// send ping to get full status update of this IC
|
// send ping to get full status update of this IC
|
||||||
err = helper.SendPing(ICUUID)
|
err = SendPing(ICUUID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,3 +243,45 @@ func newFalse() *bool {
|
||||||
b := false
|
b := false
|
||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendPing(uuid string) error {
|
||||||
|
var ping Action
|
||||||
|
ping.Act = "ping"
|
||||||
|
|
||||||
|
payload, err := json.Marshal(ping)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if session != nil {
|
||||||
|
if session.IsReady {
|
||||||
|
err = session.Send(payload, uuid)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("could not send ping, AMQP session not ready")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("could not send ping, AMQP session is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendActionAMQP(action Action, destinationUUID string) error {
|
||||||
|
|
||||||
|
payload, err := json.Marshal(action)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if session != nil {
|
||||||
|
if session.IsReady {
|
||||||
|
err = session.Send(payload, destinationUUID)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("could not send action, AMQP session is not ready")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("could not send action, AMQP session is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -140,7 +140,7 @@ func queryVillasNodeGateway(ic *database.InfrastructureComponent) error {
|
||||||
|
|
||||||
// create the update and update IC in DB
|
// create the update and update IC in DB
|
||||||
var x InfrastructureComponent
|
var x InfrastructureComponent
|
||||||
err = x.byID(ic.ID)
|
err = x.ByID(ic.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get villas-node gateway by ID %s (%s): %w", ic.Name, ic.UUID, err)
|
return fmt.Errorf("failed to get villas-node gateway by ID %s (%s): %w", ic.Name, ic.UUID, err)
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ func queryVillasRelayGateway(ic *database.InfrastructureComponent) error {
|
||||||
|
|
||||||
// create the update and update IC in DB
|
// create the update and update IC in DB
|
||||||
var x InfrastructureComponent
|
var x InfrastructureComponent
|
||||||
err = x.byID(ic.ID)
|
err = x.ByID(ic.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get villas-relay manager by ID %s (%s): %w", ic.Name, ic.UUID, err)
|
return fmt.Errorf("failed to get villas-relay manager by ID %s (%s): %w", ic.Name, ic.UUID, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,13 @@ func RegisterICEndpoints(r *gin.RouterGroup) {
|
||||||
r.GET("/:ICID", getIC)
|
r.GET("/:ICID", getIC)
|
||||||
r.DELETE("/:ICID", deleteIC)
|
r.DELETE("/:ICID", deleteIC)
|
||||||
r.GET("/:ICID/configs", getConfigsOfIC)
|
r.GET("/:ICID/configs", getConfigsOfIC)
|
||||||
|
r.POST("/:ICID/action", sendActionToIC)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterAMQPEndpoint(r *gin.RouterGroup) {
|
var session *helper.AMQPsession
|
||||||
r.POST("/:ICID/action", sendActionToIC)
|
|
||||||
|
func SetAMQPSession(s *helper.AMQPsession) {
|
||||||
|
session = s
|
||||||
}
|
}
|
||||||
|
|
||||||
// getICs godoc
|
// getICs godoc
|
||||||
|
@ -83,7 +86,7 @@ func getICs(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func addIC(c *gin.Context) {
|
func addIC(c *gin.Context) {
|
||||||
|
|
||||||
ok, _ := CheckPermissions(c, database.ModelInfrastructureComponent, database.Create, false)
|
ok, _ := database.CheckICPermissions(c, database.ModelInfrastructureComponent, database.Create, false)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -144,11 +147,14 @@ func addIC(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func updateIC(c *gin.Context) {
|
func updateIC(c *gin.Context) {
|
||||||
|
|
||||||
ok, oldIC := CheckPermissions(c, database.ModelInfrastructureComponent, database.Update, true)
|
ok, oldIC_r := database.CheckICPermissions(c, database.ModelInfrastructureComponent, database.Update, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldIC InfrastructureComponent
|
||||||
|
oldIC.InfrastructureComponent = oldIC_r
|
||||||
|
|
||||||
if oldIC.ManagedExternally {
|
if oldIC.ManagedExternally {
|
||||||
helper.BadRequestError(c, "Cannot update externally managed component via API")
|
helper.BadRequestError(c, "Cannot update externally managed component via API")
|
||||||
return
|
return
|
||||||
|
@ -193,12 +199,12 @@ func updateIC(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getIC(c *gin.Context) {
|
func getIC(c *gin.Context) {
|
||||||
|
|
||||||
ok, s := CheckPermissions(c, database.ModelInfrastructureComponent, database.Read, true)
|
ok, s := database.CheckICPermissions(c, database.ModelInfrastructureComponent, database.Read, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"ic": s.InfrastructureComponent})
|
c.JSON(http.StatusOK, gin.H{"ic": s})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteIC godoc
|
// deleteIC godoc
|
||||||
|
@ -216,11 +222,14 @@ func getIC(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteIC(c *gin.Context) {
|
func deleteIC(c *gin.Context) {
|
||||||
|
|
||||||
ok, s := CheckPermissions(c, database.ModelInfrastructureComponent, database.Delete, true)
|
ok, s_r := database.CheckICPermissions(c, database.ModelInfrastructureComponent, database.Delete, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var s InfrastructureComponent
|
||||||
|
s.InfrastructureComponent = s_r
|
||||||
|
|
||||||
// Check if IC is managed externally
|
// Check if IC is managed externally
|
||||||
if s.ManagedExternally {
|
if s.ManagedExternally {
|
||||||
// if so: refuse deletion
|
// if so: refuse deletion
|
||||||
|
@ -255,11 +264,14 @@ func deleteIC(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getConfigsOfIC(c *gin.Context) {
|
func getConfigsOfIC(c *gin.Context) {
|
||||||
|
|
||||||
ok, s := CheckPermissions(c, database.ModelInfrastructureComponent, database.Read, true)
|
ok, s_r := database.CheckICPermissions(c, database.ModelInfrastructureComponent, database.Read, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var s InfrastructureComponent
|
||||||
|
s.InfrastructureComponent = s_r
|
||||||
|
|
||||||
// get all associated configurations
|
// get all associated configurations
|
||||||
allConfigs, _, err := s.getConfigs()
|
allConfigs, _, err := s.getConfigs()
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
|
@ -284,12 +296,12 @@ func getConfigsOfIC(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func sendActionToIC(c *gin.Context) {
|
func sendActionToIC(c *gin.Context) {
|
||||||
|
|
||||||
ok, s := CheckPermissions(c, database.ModelInfrastructureComponentAction, database.Update, true)
|
ok, s := database.CheckICPermissions(c, database.ModelInfrastructureComponentAction, database.Update, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var actions []helper.Action
|
var actions []Action
|
||||||
err := c.BindJSON(&actions)
|
err := c.BindJSON(&actions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.BadRequestError(c, "Error binding form data to JSON: "+err.Error())
|
helper.BadRequestError(c, "Error binding form data to JSON: "+err.Error())
|
||||||
|
@ -300,7 +312,7 @@ func sendActionToIC(c *gin.Context) {
|
||||||
if (action.Act == "delete" || action.Act == "create") && s.Category != "manager" {
|
if (action.Act == "delete" || action.Act == "create") && s.Category != "manager" {
|
||||||
helper.BadRequestError(c, "cannot send a delete or create action to an IC of category "+s.Category)
|
helper.BadRequestError(c, "cannot send a delete or create action to an IC of category "+s.Category)
|
||||||
}
|
}
|
||||||
err = helper.SendActionAMQP(action, s.UUID)
|
err = sendActionAMQP(action, s.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.InternalServerError(c, "Unable to send actions to IC: "+err.Error())
|
helper.InternalServerError(c, "Unable to send actions to IC: "+err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -35,7 +35,7 @@ func (s *InfrastructureComponent) save() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InfrastructureComponent) byID(id uint) error {
|
func (s *InfrastructureComponent) ByID(id uint) error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
err := db.Find(s, id).Error
|
err := db.Find(s, id).Error
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
/** InfrastructureComponent package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
package infrastructure_component
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckPermissions(c *gin.Context, modeltype database.ModelName, operation database.CRUD, hasID bool) (bool, InfrastructureComponent) {
|
|
||||||
|
|
||||||
var s InfrastructureComponent
|
|
||||||
|
|
||||||
err := database.ValidateRole(c, modeltype, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of infrastructure component failed): %v", err.Error()))
|
|
||||||
return false, s
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasID {
|
|
||||||
// Get the ID of the infrastructure component from the context
|
|
||||||
ICID, err := helper.GetIDOfElement(c, "ICID", "path", -1)
|
|
||||||
if err != nil {
|
|
||||||
return false, s
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.byID(uint(ICID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, s
|
|
||||||
}
|
|
|
@ -24,13 +24,10 @@ package infrastructure_component
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/streadway/amqp"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
component_configuration "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
|
component_configuration "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||||
|
@ -133,12 +130,6 @@ func TestMain(m *testing.M) {
|
||||||
// that can be associated with a new component configuration
|
// that can be associated with a new component configuration
|
||||||
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||||
|
|
||||||
// check AMQP connection
|
|
||||||
err = helper.CheckConnection()
|
|
||||||
if err.Error() != "connection is nil" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect AMQP client
|
// connect AMQP client
|
||||||
// Make sure that AMQP_HOST, AMQP_USER, AMQP_PASS are set
|
// Make sure that AMQP_HOST, AMQP_USER, AMQP_PASS are set
|
||||||
host, _ := configuration.GlobalConfig.String("amqp.host")
|
host, _ := configuration.GlobalConfig.String("amqp.host")
|
||||||
|
@ -148,11 +139,8 @@ func TestMain(m *testing.M) {
|
||||||
|
|
||||||
// AMQP Connection startup is tested here
|
// AMQP Connection startup is tested here
|
||||||
// Not repeated in other tests because it is only needed once
|
// Not repeated in other tests because it is only needed once
|
||||||
err = StartAMQP(amqpURI, api)
|
session = helper.NewAMQPSession("villas-test-session", amqpURI, "villas", ProcessMessage)
|
||||||
if err != nil {
|
SetAMQPSession(session)
|
||||||
log.Println("unable to connect to AMQP")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
@ -160,10 +148,10 @@ func TestMain(m *testing.M) {
|
||||||
func TestAddICAsAdmin(t *testing.T) {
|
func TestAddICAsAdmin(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST with non JSON body
|
// try to POST with non JSON body
|
||||||
|
@ -225,10 +213,10 @@ func TestAddICAsAdmin(t *testing.T) {
|
||||||
func TestAddICAsUser(t *testing.T) {
|
func TestAddICAsUser(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as user
|
// authenticate as user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST ic/ $newIC
|
// test POST ic/ $newIC
|
||||||
|
@ -244,10 +232,10 @@ func TestAddICAsUser(t *testing.T) {
|
||||||
func TestUpdateICAsAdmin(t *testing.T) {
|
func TestUpdateICAsAdmin(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST ic/ $newIC
|
// test POST ic/ $newIC
|
||||||
|
@ -306,23 +294,14 @@ func TestUpdateICAsAdmin(t *testing.T) {
|
||||||
payload, err := json.Marshal(update)
|
payload, err := json.Marshal(update)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
//var headers map[string]interface{}
|
||||||
headers["uuid"] = newIC2.Manager // set uuid
|
//headers = make(map[string]interface{}) // empty map
|
||||||
|
//headers["uuid"] = newIC2.Manager // set uuid
|
||||||
|
|
||||||
msg := amqp.Publishing{
|
err = session.CheckConnection()
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = helper.CheckConnection()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = helper.PublishAMQP(msg)
|
err = session.Send(payload, newIC2.Manager)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Wait until externally managed IC is created (happens async)
|
// Wait until externally managed IC is created (happens async)
|
||||||
|
@ -342,10 +321,10 @@ func TestUpdateICAsAdmin(t *testing.T) {
|
||||||
func TestUpdateICAsUser(t *testing.T) {
|
func TestUpdateICAsUser(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST ic/ $newIC
|
// test POST ic/ $newIC
|
||||||
|
@ -359,7 +338,7 @@ func TestUpdateICAsUser(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as user
|
// authenticate as user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Test PUT IC
|
// Test PUT IC
|
||||||
|
@ -375,10 +354,10 @@ func TestUpdateICAsUser(t *testing.T) {
|
||||||
func TestDeleteICAsAdmin(t *testing.T) {
|
func TestDeleteICAsAdmin(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST ic/ $newIC
|
// test POST ic/ $newIC
|
||||||
|
@ -425,23 +404,10 @@ func TestDeleteICAsAdmin(t *testing.T) {
|
||||||
payload, err := json.Marshal(update)
|
payload, err := json.Marshal(update)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
err = session.CheckConnection()
|
||||||
headers["uuid"] = newIC2.UUID // set uuid
|
|
||||||
|
|
||||||
msg := amqp.Publishing{
|
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = helper.CheckConnection()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = helper.PublishAMQP(msg)
|
err = session.Send(payload, newIC2.UUID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Wait until externally managed IC is created (happens async)
|
// Wait until externally managed IC is created (happens async)
|
||||||
|
@ -465,10 +431,10 @@ func TestDeleteICAsAdmin(t *testing.T) {
|
||||||
func TestDeleteICAsUser(t *testing.T) {
|
func TestDeleteICAsUser(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST ic/ $newIC
|
// test POST ic/ $newIC
|
||||||
|
@ -482,7 +448,7 @@ func TestDeleteICAsUser(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as user
|
// authenticate as user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Test DELETE ICs
|
// Test DELETE ICs
|
||||||
|
@ -497,10 +463,10 @@ func TestDeleteICAsUser(t *testing.T) {
|
||||||
func TestGetAllICs(t *testing.T) {
|
func TestGetAllICs(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get the length of the GET all ICs response for user
|
// get the length of the GET all ICs response for user
|
||||||
|
@ -529,7 +495,7 @@ func TestGetAllICs(t *testing.T) {
|
||||||
assert.Equal(t, finalNumber, initialNumber+2)
|
assert.Equal(t, finalNumber, initialNumber+2)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get the length of the GET all ICs response again
|
// get the length of the GET all ICs response again
|
||||||
|
@ -543,10 +509,10 @@ func TestGetAllICs(t *testing.T) {
|
||||||
func TestGetConfigsOfIC(t *testing.T) {
|
func TestGetConfigsOfIC(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST ic/ $newICA
|
// test POST ic/ $newICA
|
||||||
|
@ -568,7 +534,7 @@ func TestGetConfigsOfIC(t *testing.T) {
|
||||||
assert.Equal(t, 0, numberOfConfigs)
|
assert.Equal(t, 0, numberOfConfigs)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test GET ic/ID/configs
|
// test GET ic/ID/configs
|
||||||
|
@ -590,10 +556,10 @@ func TestGetConfigsOfIC(t *testing.T) {
|
||||||
func TestSendActionToIC(t *testing.T) {
|
func TestSendActionToIC(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST ic/ $newICA
|
// test POST ic/ $newICA
|
||||||
|
@ -607,7 +573,7 @@ func TestSendActionToIC(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create action to be sent to IC
|
// create action to be sent to IC
|
||||||
action1 := helper.Action{
|
action1 := Action{
|
||||||
Act: "start",
|
Act: "start",
|
||||||
When: time.Now().Unix(),
|
When: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
|
@ -620,7 +586,7 @@ func TestSendActionToIC(t *testing.T) {
|
||||||
|
|
||||||
paramsRaw, _ := json.Marshal(¶ms)
|
paramsRaw, _ := json.Marshal(¶ms)
|
||||||
action1.Parameters = paramsRaw
|
action1.Parameters = paramsRaw
|
||||||
actions := [1]helper.Action{action1}
|
actions := [1]Action{action1}
|
||||||
|
|
||||||
// Send action to IC
|
// Send action to IC
|
||||||
code, resp, err = helper.TestEndpoint(router, token,
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
|
@ -639,10 +605,10 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// fake an IC update message
|
// fake an IC update message
|
||||||
|
@ -652,23 +618,10 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
||||||
payload, err := json.Marshal(update)
|
payload, err := json.Marshal(update)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
err = session.CheckConnection()
|
||||||
headers["uuid"] = newIC1.Manager // set uuid
|
|
||||||
|
|
||||||
msg := amqp.Publishing{
|
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = helper.CheckConnection()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = helper.PublishAMQP(msg)
|
err = session.Send(payload, newIC1.Manager)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
time.Sleep(waitingTime * time.Second)
|
time.Sleep(waitingTime * time.Second)
|
||||||
|
@ -694,20 +647,7 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
||||||
payload, err = json.Marshal(update)
|
payload, err = json.Marshal(update)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var headersA map[string]interface{} = make(map[string]interface{}) // empty map
|
err = session.Send(payload, newIC1.Manager)
|
||||||
headersA["uuid"] = newIC1.Manager
|
|
||||||
|
|
||||||
msg = amqp.Publishing{
|
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
Headers: headersA,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = helper.PublishAMQP(msg)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
time.Sleep(waitingTime * time.Second)
|
time.Sleep(waitingTime * time.Second)
|
||||||
|
@ -723,17 +663,7 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
||||||
payload, err = json.Marshal(update)
|
payload, err = json.Marshal(update)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
msg = amqp.Publishing{
|
err = session.Send(payload, newIC1.Manager)
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
Headers: headersA,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = helper.PublishAMQP(msg)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
time.Sleep(waitingTime * time.Second)
|
time.Sleep(waitingTime * time.Second)
|
||||||
|
@ -749,10 +679,10 @@ func TestDeleteICViaAMQPRecv(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// fake an IC update message
|
// fake an IC update message
|
||||||
|
@ -773,22 +703,10 @@ func TestDeleteICViaAMQPRecv(t *testing.T) {
|
||||||
payload, err := json.Marshal(update)
|
payload, err := json.Marshal(update)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
err = session.CheckConnection()
|
||||||
headers["uuid"] = newIC1.Manager // set uuid
|
|
||||||
|
|
||||||
msg := amqp.Publishing{
|
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = helper.CheckConnection()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = helper.PublishAMQP(msg)
|
|
||||||
|
err = session.Send(payload, newIC1.Manager)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
time.Sleep(waitingTime * time.Second)
|
time.Sleep(waitingTime * time.Second)
|
||||||
|
@ -847,18 +765,7 @@ func TestDeleteICViaAMQPRecv(t *testing.T) {
|
||||||
payload, err = json.Marshal(update)
|
payload, err = json.Marshal(update)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
msg = amqp.Publishing{
|
err = session.Send(payload, newIC1.Manager)
|
||||||
DeliveryMode: 2,
|
|
||||||
Timestamp: time.Now(),
|
|
||||||
ContentType: "application/json",
|
|
||||||
ContentEncoding: "utf-8",
|
|
||||||
Priority: 0,
|
|
||||||
Body: payload,
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to delete IC (should not work immediately because IC is still associated with component config)
|
|
||||||
err = helper.PublishAMQP(msg)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
time.Sleep(waitingTime * time.Second)
|
time.Sleep(waitingTime * time.Second)
|
||||||
|
|
|
@ -151,7 +151,7 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var Admin = helper.Credentials{
|
var Admin = database.Credentials{
|
||||||
Username: adminName,
|
Username: adminName,
|
||||||
Password: adminPW,
|
Password: adminPW,
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
infrastructure_component "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -55,30 +53,6 @@ func TestMain(m *testing.M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The order of test functions is important here
|
|
||||||
* 1. Start and connect AMQP
|
|
||||||
* 2. Register endpoints
|
|
||||||
* 3. Add test data
|
|
||||||
*/
|
|
||||||
|
|
||||||
func TestStartAMQP(t *testing.T) {
|
|
||||||
// connect AMQP client
|
|
||||||
// Make sure that AMQP_HOST, AMQP_USER, AMQP_PASS are set
|
|
||||||
host, err := configuration.GlobalConfig.String("amqp.host")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
user, err := configuration.GlobalConfig.String("amqp.user")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
pass, err := configuration.GlobalConfig.String("amqp.pass")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
amqpURI := "amqp://" + user + ":" + pass + "@" + host
|
|
||||||
|
|
||||||
// AMQP Connection startup is tested here
|
|
||||||
// Not repeated in other tests because it is only needed once
|
|
||||||
err = infrastructure_component.StartAMQP(amqpURI, api)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRegisterEndpoints(t *testing.T) {
|
func TestRegisterEndpoints(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/file"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/file"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -57,7 +56,7 @@ func RegisterResultEndpoints(r *gin.RouterGroup) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getResults(c *gin.Context) {
|
func getResults(c *gin.Context) {
|
||||||
|
|
||||||
ok, sco := scenario.CheckPermissions(c, database.Read, "query", -1)
|
ok, sco := database.CheckScenarioPermissions(c, database.Read, "query", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -103,7 +102,7 @@ func addResult(c *gin.Context) {
|
||||||
newResult := req.createResult()
|
newResult := req.createResult()
|
||||||
|
|
||||||
// Check if user is allowed to modify scenario specified in request
|
// Check if user is allowed to modify scenario specified in request
|
||||||
ok, _ := scenario.CheckPermissions(c, database.Update, "body", int(newResult.ScenarioID))
|
ok, _ := database.CheckScenarioPermissions(c, database.Update, "body", int(newResult.ScenarioID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -133,11 +132,14 @@ func addResult(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func updateResult(c *gin.Context) {
|
func updateResult(c *gin.Context) {
|
||||||
|
|
||||||
ok, oldResult := checkPermissions(c, database.Update, "path", -1)
|
ok, oldResult_r := database.CheckResultPermissions(c, database.Update, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldResult Result
|
||||||
|
oldResult.Result = oldResult_r
|
||||||
|
|
||||||
var req updateResultRequest
|
var req updateResultRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.BadRequestError(c, err.Error())
|
helper.BadRequestError(c, err.Error())
|
||||||
|
@ -175,12 +177,12 @@ func updateResult(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getResult(c *gin.Context) {
|
func getResult(c *gin.Context) {
|
||||||
|
|
||||||
ok, result := checkPermissions(c, database.Read, "path", -1)
|
ok, result := database.CheckResultPermissions(c, database.Read, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"result": result.Result})
|
c.JSON(http.StatusOK, gin.H{"result": result})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteResult godoc
|
// deleteResult godoc
|
||||||
|
@ -197,13 +199,16 @@ func getResult(c *gin.Context) {
|
||||||
// @Router /results/{resultID} [delete]
|
// @Router /results/{resultID} [delete]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteResult(c *gin.Context) {
|
func deleteResult(c *gin.Context) {
|
||||||
ok, result := checkPermissions(c, database.Delete, "path", -1)
|
ok, result_r := database.CheckResultPermissions(c, database.Delete, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var result Result
|
||||||
|
result.Result = result_r
|
||||||
|
|
||||||
// Check if user is allowed to modify scenario associated with result
|
// Check if user is allowed to modify scenario associated with result
|
||||||
ok, _ = scenario.CheckPermissions(c, database.Update, "body", int(result.ScenarioID))
|
ok, _ = database.CheckScenarioPermissions(c, database.Update, "body", int(result.ScenarioID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -240,13 +245,16 @@ func deleteResult(c *gin.Context) {
|
||||||
// @Router /results/{resultID}/file [post]
|
// @Router /results/{resultID}/file [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func addResultFile(c *gin.Context) {
|
func addResultFile(c *gin.Context) {
|
||||||
ok, result := checkPermissions(c, database.Update, "path", -1)
|
ok, result_r := database.CheckResultPermissions(c, database.Update, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var result Result
|
||||||
|
result.Result = result_r
|
||||||
|
|
||||||
// Check if user is allowed to modify scenario associated with result
|
// Check if user is allowed to modify scenario associated with result
|
||||||
ok, sco := scenario.CheckPermissions(c, database.Update, "body", int(result.ScenarioID))
|
ok, sco := database.CheckScenarioPermissions(c, database.Update, "body", int(result.ScenarioID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -290,18 +298,24 @@ func addResultFile(c *gin.Context) {
|
||||||
func deleteResultFile(c *gin.Context) {
|
func deleteResultFile(c *gin.Context) {
|
||||||
|
|
||||||
// check access
|
// check access
|
||||||
ok, result := checkPermissions(c, database.Update, "path", -1)
|
ok, result_r := database.CheckResultPermissions(c, database.Update, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, f := file.CheckPermissions(c, database.Delete)
|
var result Result
|
||||||
|
result.Result = result_r
|
||||||
|
|
||||||
|
ok, f_r := database.CheckFilePermissions(c, database.Delete)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var f file.File
|
||||||
|
f.File = f_r
|
||||||
|
|
||||||
// Check if user is allowed to modify scenario associated with result
|
// Check if user is allowed to modify scenario associated with result
|
||||||
ok, _ = scenario.CheckPermissions(c, database.Update, "body", int(result.ScenarioID))
|
ok, _ = database.CheckScenarioPermissions(c, database.Update, "body", int(result.ScenarioID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/file"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/file"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
|
@ -51,8 +50,8 @@ func (r *Result) ByID(id uint) error {
|
||||||
|
|
||||||
func (r *Result) addToScenario() error {
|
func (r *Result) addToScenario() error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var sco scenario.Scenario
|
var sco database.Scenario
|
||||||
err := sco.ByID(r.ScenarioID)
|
err := db.Find(&sco, r.ScenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -85,8 +84,8 @@ func (r *Result) update(modifiedResult Result) error {
|
||||||
func (r *Result) delete() error {
|
func (r *Result) delete() error {
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var sco scenario.Scenario
|
var sco database.Scenario
|
||||||
err := sco.ByID(r.ScenarioID)
|
err := db.Find(&sco, r.ScenarioID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
/** Result package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
|
|
||||||
package result
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkPermissions(c *gin.Context, operation database.CRUD, resultIDSource string, resultIDBody int) (bool, Result) {
|
|
||||||
|
|
||||||
var result Result
|
|
||||||
|
|
||||||
err := database.ValidateRole(c, database.ModelResult, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation failed): %v", err.Error()))
|
|
||||||
return false, result
|
|
||||||
}
|
|
||||||
|
|
||||||
resultID, err := helper.GetIDOfElement(c, "resultID", resultIDSource, resultIDBody)
|
|
||||||
if err != nil {
|
|
||||||
return false, result
|
|
||||||
}
|
|
||||||
|
|
||||||
err = result.ByID(uint(resultID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, result
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, _ := scenario.CheckPermissions(c, operation, "body", int(result.ScenarioID))
|
|
||||||
if !ok {
|
|
||||||
return false, result
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, result
|
|
||||||
}
|
|
|
@ -70,10 +70,10 @@ var newResult = ResultRequest{
|
||||||
func addScenario() (scenarioID uint) {
|
func addScenario() (scenarioID uint) {
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
helper.AuthenticateForTest(router, helper.AdminCredentials)
|
_, _ = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, _ := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, _ := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
|
|
||||||
// POST $newScenario
|
// POST $newScenario
|
||||||
newScenario := ScenarioRequest{
|
newScenario := ScenarioRequest{
|
||||||
|
@ -123,14 +123,14 @@ func TestGetAllResultsOfScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario
|
// by adding a scenario
|
||||||
scenarioID := addScenario()
|
scenarioID := addScenario()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST newResult
|
// test POST newResult
|
||||||
|
@ -154,7 +154,7 @@ func TestGetAllResultsOfScenario(t *testing.T) {
|
||||||
assert.Equal(t, 1, NumberOfConfigs)
|
assert.Equal(t, 1, NumberOfConfigs)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to scenario
|
// authenticate as normal userB who has no access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to get results without access
|
// try to get results without access
|
||||||
|
@ -170,7 +170,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario
|
// by adding a scenario
|
||||||
|
@ -182,7 +182,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
newResult.ScenarioID = scenarioID
|
newResult.ScenarioID = scenarioID
|
||||||
newResult.ConfigSnapshots = confSnapshots
|
newResult.ConfigSnapshots = confSnapshots
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST with no access
|
// try to POST with no access
|
||||||
|
@ -193,7 +193,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST non JSON body
|
// try to POST non JSON body
|
||||||
|
@ -238,7 +238,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Try to GET the newResult with no access
|
// Try to GET the newResult with no access
|
||||||
|
@ -263,7 +263,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user who has access to result
|
// authenticate as guest user who has access to result
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT as guest
|
// try to PUT as guest
|
||||||
|
@ -274,7 +274,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT a non JSON body
|
// try to PUT a non JSON body
|
||||||
|
@ -304,7 +304,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
newResult.Description = updatedResult.Description
|
newResult.Description = updatedResult.Description
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to DELETE with no access
|
// try to DELETE with no access
|
||||||
|
@ -315,7 +315,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the results returned for scenario
|
// Count the number of all the results returned for scenario
|
||||||
|
@ -345,7 +345,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
||||||
func TestAddDeleteResultFile(t *testing.T) {
|
func TestAddDeleteResultFile(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario
|
// by adding a scenario
|
||||||
|
@ -358,7 +358,7 @@ func TestAddDeleteResultFile(t *testing.T) {
|
||||||
newResult.ScenarioID = scenarioID
|
newResult.ScenarioID = scenarioID
|
||||||
newResult.ConfigSnapshots = confSnapshots
|
newResult.ConfigSnapshots = confSnapshots
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST newResult
|
// test POST newResult
|
||||||
|
|
|
@ -22,14 +22,10 @@
|
||||||
package scenario
|
package scenario
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterScenarioEndpoints(r *gin.RouterGroup) {
|
func RegisterScenarioEndpoints(r *gin.RouterGroup) {
|
||||||
|
@ -60,15 +56,15 @@ func getScenarios(c *gin.Context) {
|
||||||
|
|
||||||
// ATTENTION: do not use c.GetInt (common.UserIDCtx) since userID is of type uint and not int
|
// ATTENTION: do not use c.GetInt (common.UserIDCtx) since userID is of type uint and not int
|
||||||
userID, _ := c.Get(database.UserIDCtx)
|
userID, _ := c.Get(database.UserIDCtx)
|
||||||
|
db := database.GetDB()
|
||||||
var u user.User
|
var u database.User
|
||||||
err := u.ByID(userID.(uint))
|
err := db.Find(&u, userID.(uint)).Error
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all scenarios for the user who issues the request
|
// get all scenarios for the user who issues the request
|
||||||
db := database.GetDB()
|
|
||||||
var scenarios []database.Scenario
|
var scenarios []database.Scenario
|
||||||
if u.Role == "Admin" { // Admin can see all scenarios
|
if u.Role == "Admin" { // Admin can see all scenarios
|
||||||
err = db.Order("ID asc").Find(&scenarios).Error
|
err = db.Order("ID asc").Find(&scenarios).Error
|
||||||
|
@ -102,16 +98,16 @@ func getScenarios(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func addScenario(c *gin.Context) {
|
func addScenario(c *gin.Context) {
|
||||||
|
|
||||||
ok, _ := CheckPermissions(c, database.Create, "none", -1)
|
ok, _ := database.CheckScenarioPermissions(c, database.Create, "none", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ATTENTION: do not use c.GetInt (common.UserIDCtx) since userID is of type uint and not int
|
// ATTENTION: do not use c.GetInt (common.UserIDCtx) since userID is of type uint and not int
|
||||||
userID, _ := c.Get(database.UserIDCtx)
|
userID, _ := c.Get(database.UserIDCtx)
|
||||||
|
db := database.GetDB()
|
||||||
var u user.User
|
var u database.User
|
||||||
err := u.ByID(userID.(uint))
|
err := db.Find(&u, userID.(uint)).Error
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -138,7 +134,7 @@ func addScenario(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add user to new scenario
|
// add user to new scenario
|
||||||
err = newScenario.addUser(&(u.User))
|
err = newScenario.addUser(&(u))
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -163,11 +159,14 @@ func addScenario(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func updateScenario(c *gin.Context) {
|
func updateScenario(c *gin.Context) {
|
||||||
|
|
||||||
ok, oldScenario := CheckPermissions(c, database.Update, "path", -1)
|
ok, oldScenario_r := database.CheckScenarioPermissions(c, database.Update, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldScenario Scenario
|
||||||
|
oldScenario.Scenario = oldScenario_r
|
||||||
|
|
||||||
// Bind the (context) with the updateScenarioRequest struct
|
// Bind the (context) with the updateScenarioRequest struct
|
||||||
var req updateScenarioRequest
|
var req updateScenarioRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
@ -208,13 +207,13 @@ func updateScenario(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getScenario(c *gin.Context) {
|
func getScenario(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := CheckPermissions(c, database.Read, "path", -1)
|
ok, so := database.CheckScenarioPermissions(c, database.Read, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return list of configIDs, dashboardIDs and userIDs per scenario
|
// TODO return list of configIDs, dashboardIDs and userIDs per scenario
|
||||||
c.JSON(http.StatusOK, gin.H{"scenario": so.Scenario})
|
c.JSON(http.StatusOK, gin.H{"scenario": so})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteScenario godoc
|
// deleteScenario godoc
|
||||||
|
@ -231,11 +230,14 @@ func getScenario(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteScenario(c *gin.Context) {
|
func deleteScenario(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := CheckPermissions(c, database.Delete, "path", -1)
|
ok, so_r := database.CheckScenarioPermissions(c, database.Delete, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var so Scenario
|
||||||
|
so.Scenario = so_r
|
||||||
|
|
||||||
err := so.delete()
|
err := so.delete()
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
|
@ -258,11 +260,14 @@ func deleteScenario(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getUsersOfScenario(c *gin.Context) {
|
func getUsersOfScenario(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := CheckPermissions(c, database.Read, "path", -1)
|
ok, so_r := database.CheckScenarioPermissions(c, database.Read, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var so Scenario
|
||||||
|
so.Scenario = so_r
|
||||||
|
|
||||||
// Find all users of scenario
|
// Find all users of scenario
|
||||||
allUsers, _, err := so.getUsers()
|
allUsers, _, err := so.getUsers()
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
|
@ -287,15 +292,18 @@ func getUsersOfScenario(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func addUserToScenario(c *gin.Context) {
|
func addUserToScenario(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := CheckPermissions(c, database.Update, "path", -1)
|
ok, so_r := database.CheckScenarioPermissions(c, database.Update, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
username := c.Request.URL.Query().Get("username")
|
var so Scenario
|
||||||
|
so.Scenario = so_r
|
||||||
|
|
||||||
var u user.User
|
username := c.Request.URL.Query().Get("username")
|
||||||
err := u.ByUsername(username)
|
var u database.User
|
||||||
|
db := database.GetDB()
|
||||||
|
err := db.Find(&u, "Username = ?", username).Error
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -305,12 +313,12 @@ func addUserToScenario(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = so.addUser(&(u.User))
|
err = so.addUser(&(u))
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"user": u.User})
|
c.JSON(http.StatusOK, gin.H{"user": u})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteUserFromScenario godoc
|
// deleteUserFromScenario godoc
|
||||||
|
@ -328,15 +336,18 @@ func addUserToScenario(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteUserFromScenario(c *gin.Context) {
|
func deleteUserFromScenario(c *gin.Context) {
|
||||||
|
|
||||||
ok, so := CheckPermissions(c, database.Update, "path", -1)
|
ok, so_r := database.CheckScenarioPermissions(c, database.Update, "path", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
username := c.Request.URL.Query().Get("username")
|
var so Scenario
|
||||||
|
so.Scenario = so_r
|
||||||
|
|
||||||
var u user.User
|
username := c.Request.URL.Query().Get("username")
|
||||||
err := u.ByUsername(username)
|
var u database.User
|
||||||
|
db := database.GetDB()
|
||||||
|
err := db.Find(&u, "Username = ?", username).Error
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -346,5 +357,5 @@ func deleteUserFromScenario(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"user": u.User})
|
c.JSON(http.StatusOK, gin.H{"user": u})
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,8 +82,8 @@ func (s *Scenario) addUser(u *database.User) error {
|
||||||
func (s *Scenario) deleteUser(username string) error {
|
func (s *Scenario) deleteUser(username string) error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
|
|
||||||
var deletedUser user.User
|
var deletedUser database.User
|
||||||
err := deletedUser.ByUsername(username)
|
err := db.Find(&deletedUser, "Username = ?", username).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -93,18 +92,18 @@ func (s *Scenario) deleteUser(username string) error {
|
||||||
|
|
||||||
if no_users > 1 {
|
if no_users > 1 {
|
||||||
// remove user from scenario
|
// remove user from scenario
|
||||||
err = db.Model(s).Association("Users").Delete(&deletedUser.User).Error
|
err = db.Model(s).Association("Users").Delete(&deletedUser).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// remove scenario from user
|
// remove scenario from user
|
||||||
err = db.Model(&deletedUser.User).Association("Scenarios").Delete(s).Error
|
err = db.Model(&deletedUser).Association("Scenarios").Delete(s).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// There is only one associated user
|
// There is only one associated user
|
||||||
var remainingUser user.User
|
var remainingUser database.User
|
||||||
err = db.Model(s).Related(&remainingUser, "Users").Error
|
err = db.Model(s).Related(&remainingUser, "Users").Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -155,33 +154,3 @@ func (s *Scenario) delete() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scenario) checkAccess(userID uint, operation database.CRUD) bool {
|
|
||||||
|
|
||||||
db := database.GetDB()
|
|
||||||
u := database.User{}
|
|
||||||
|
|
||||||
err := db.Find(&u, userID).Error
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Role == "Admin" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarioUser := database.User{}
|
|
||||||
err = db.Order("ID asc").Model(s).Where("ID = ?", userID).Related(&scenarioUser, "Users").Error
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !scenarioUser.Active {
|
|
||||||
return false
|
|
||||||
} else if s.IsLocked && operation != database.Read {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
/** Scenario package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
package scenario
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckPermissions(c *gin.Context, operation database.CRUD, scenarioIDsource string, scenarioIDbody int) (bool, Scenario) {
|
|
||||||
|
|
||||||
var so Scenario
|
|
||||||
|
|
||||||
err := database.ValidateRole(c, database.ModelScenario, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of scenario failed): %v", err))
|
|
||||||
return false, so
|
|
||||||
}
|
|
||||||
|
|
||||||
if operation == database.Create || (operation == database.Read && scenarioIDsource == "none") {
|
|
||||||
return true, so
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarioID, err := helper.GetIDOfElement(c, "scenarioID", scenarioIDsource, scenarioIDbody)
|
|
||||||
if err != nil {
|
|
||||||
return false, so
|
|
||||||
}
|
|
||||||
|
|
||||||
userID, _ := c.Get(database.UserIDCtx)
|
|
||||||
|
|
||||||
err = so.ByID(uint(scenarioID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, so
|
|
||||||
}
|
|
||||||
|
|
||||||
if !so.checkAccess(userID.(uint), operation) {
|
|
||||||
helper.UnprocessableEntityError(c, "Access denied (user has no access or scenario is locked).")
|
|
||||||
return false, so
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, so
|
|
||||||
}
|
|
|
@ -57,6 +57,7 @@ var newScenario1 = ScenarioRequest{
|
||||||
StartParameters: postgres.Jsonb{
|
StartParameters: postgres.Jsonb{
|
||||||
RawMessage: json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`),
|
RawMessage: json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`),
|
||||||
},
|
},
|
||||||
|
IsLocked: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
var newScenario2 = ScenarioRequest{
|
var newScenario2 = ScenarioRequest{
|
||||||
|
@ -64,6 +65,7 @@ var newScenario2 = ScenarioRequest{
|
||||||
StartParameters: postgres.Jsonb{
|
StartParameters: postgres.Jsonb{
|
||||||
RawMessage: json.RawMessage(`{"parameter1" : "testValue1B", "parameter2" : "testValue2B", "parameter3" : 55}`),
|
RawMessage: json.RawMessage(`{"parameter1" : "testValue1B", "parameter2" : "testValue2B", "parameter3" : 55}`),
|
||||||
},
|
},
|
||||||
|
IsLocked: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
@ -95,10 +97,10 @@ func TestAddScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST with non JSON body
|
// try to POST with non JSON body
|
||||||
|
@ -151,7 +153,7 @@ func TestAddScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user
|
// authenticate as guest user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to add scenario as guest user
|
// try to add scenario as guest user
|
||||||
|
@ -162,7 +164,7 @@ func TestAddScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as userB who has no access to the added scenario
|
// authenticate as userB who has no access to the added scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to GET a scenario to which user B has no access
|
// try to GET a scenario to which user B has no access
|
||||||
|
@ -173,7 +175,7 @@ func TestAddScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as admin user who has no access to everything
|
// authenticate as admin user who has no access to everything
|
||||||
token, err = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to GET a scenario that is not created by admin user; should work anyway
|
// try to GET a scenario that is not created by admin user; should work anyway
|
||||||
|
@ -187,10 +189,10 @@ func TestUpdateScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario
|
// test POST scenarios/ $newScenario
|
||||||
|
@ -258,7 +260,7 @@ func TestUpdateScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as admin user who has no access to everything
|
// authenticate as admin user who has no access to everything
|
||||||
token, err = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// changed locked state of scenario as admin user (should work)
|
// changed locked state of scenario as admin user (should work)
|
||||||
|
@ -294,7 +296,7 @@ func TestUpdateScenario(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Get the updatedScenario
|
// Get the updatedScenario
|
||||||
|
@ -320,10 +322,10 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get the length of the GET all scenarios response for admin
|
// get the length of the GET all scenarios response for admin
|
||||||
|
@ -332,7 +334,7 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as normal userB
|
// authenticate as normal userB
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario1
|
// test POST scenarios/ $newScenario1
|
||||||
|
@ -342,7 +344,7 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userA
|
// authenticate as normal userA
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario2
|
// test POST scenarios/ $newScenario2
|
||||||
|
@ -352,7 +354,7 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get the length of the GET all scenarios response again
|
// get the length of the GET all scenarios response again
|
||||||
|
@ -367,10 +369,10 @@ func TestGetAllScenariosAsUser(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal userB
|
// authenticate as normal userB
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get the length of the GET all scenarios response for userB
|
// get the length of the GET all scenarios response for userB
|
||||||
|
@ -385,7 +387,7 @@ func TestGetAllScenariosAsUser(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userA
|
// authenticate as normal userA
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario1
|
// test POST scenarios/ $newScenario1
|
||||||
|
@ -395,7 +397,7 @@ func TestGetAllScenariosAsUser(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userB
|
// authenticate as normal userB
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get the length of the GET all scenarios response again
|
// get the length of the GET all scenarios response again
|
||||||
|
@ -410,10 +412,10 @@ func TestDeleteScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario
|
// test POST scenarios/ $newScenario
|
||||||
|
@ -433,7 +435,7 @@ func TestDeleteScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user
|
// authenticate as guest user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to delete scenario as guest
|
// try to delete scenario as guest
|
||||||
|
@ -444,7 +446,7 @@ func TestDeleteScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the scenarios returned for userA
|
// Count the number of all the scenarios returned for userA
|
||||||
|
@ -474,10 +476,10 @@ func TestAddUserToScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario
|
// test POST scenarios/ $newScenario
|
||||||
|
@ -491,7 +493,7 @@ func TestAddUserToScenario(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to add new user User_C to scenario as userB
|
// try to add new user User_C to scenario as userB
|
||||||
|
@ -509,7 +511,7 @@ func TestAddUserToScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the users returned for newScenario
|
// Count the number of all the users returned for newScenario
|
||||||
|
@ -526,9 +528,9 @@ func TestAddUserToScenario(t *testing.T) {
|
||||||
|
|
||||||
// Compare resp to userB
|
// Compare resp to userB
|
||||||
userB := UserRequest{
|
userB := UserRequest{
|
||||||
Username: helper.UserB.Username,
|
Username: database.UserB.Username,
|
||||||
Mail: helper.UserB.Mail,
|
Mail: database.UserB.Mail,
|
||||||
Role: helper.UserB.Role,
|
Role: database.UserB.Role,
|
||||||
}
|
}
|
||||||
err = helper.CompareResponse(resp, helper.KeyModels{"user": userB})
|
err = helper.CompareResponse(resp, helper.KeyModels{"user": userB})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -551,10 +553,10 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario
|
// test POST scenarios/ $newScenario
|
||||||
|
@ -568,7 +570,7 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to get all users of new scenario with userB
|
// try to get all users of new scenario with userB
|
||||||
|
@ -579,7 +581,7 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the users returned for newScenario
|
// Count the number of all the users returned for newScenario
|
||||||
|
@ -601,7 +603,7 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
||||||
assert.Equal(t, finalNumber, initialNumber+1)
|
assert.Equal(t, finalNumber, initialNumber+1)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, err = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// set userB as inactive
|
// set userB as inactive
|
||||||
|
@ -612,7 +614,7 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count AGAIN the number of all the users returned for newScenario
|
// Count AGAIN the number of all the users returned for newScenario
|
||||||
|
@ -626,10 +628,10 @@ func TestRemoveUserFromScenario(t *testing.T) {
|
||||||
|
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST scenarios/ $newScenario
|
// test POST scenarios/ $newScenario
|
||||||
|
@ -649,7 +651,7 @@ func TestRemoveUserFromScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to delete userC from new scenario
|
// try to delete userC from new scenario
|
||||||
|
@ -660,7 +662,7 @@ func TestRemoveUserFromScenario(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the users returned for newScenario
|
// Count the number of all the users returned for newScenario
|
||||||
|
@ -677,9 +679,9 @@ func TestRemoveUserFromScenario(t *testing.T) {
|
||||||
|
|
||||||
// Compare DELETE's response with UserC's data
|
// Compare DELETE's response with UserC's data
|
||||||
userC := UserRequest{
|
userC := UserRequest{
|
||||||
Username: helper.UserC.Username,
|
Username: database.UserC.Username,
|
||||||
Mail: helper.UserC.Mail,
|
Mail: database.UserC.Mail,
|
||||||
Role: helper.UserC.Role,
|
Role: database.UserC.Role,
|
||||||
}
|
}
|
||||||
err = helper.CompareResponse(resp, helper.KeyModels{"user": userC})
|
err = helper.CompareResponse(resp, helper.KeyModels{"user": userC})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
component_configuration "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterSignalEndpoints(r *gin.RouterGroup) {
|
func RegisterSignalEndpoints(r *gin.RouterGroup) {
|
||||||
|
@ -55,7 +54,7 @@ func RegisterSignalEndpoints(r *gin.RouterGroup) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getSignals(c *gin.Context) {
|
func getSignals(c *gin.Context) {
|
||||||
|
|
||||||
ok, m := component_configuration.CheckPermissions(c, database.Read, "query", -1)
|
ok, m := database.CheckComponentConfigPermissions(c, database.Read, "query", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -111,13 +110,13 @@ func addSignal(c *gin.Context) {
|
||||||
// Create the new signal from the request
|
// Create the new signal from the request
|
||||||
newSignal := req.createSignal()
|
newSignal := req.createSignal()
|
||||||
|
|
||||||
ok, _ := component_configuration.CheckPermissions(c, database.Update, "body", int(newSignal.ConfigID))
|
ok, _ := database.CheckComponentConfigPermissions(c, database.Update, "body", int(newSignal.ConfigID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add signal to component configuration
|
// Add signal to component configuration
|
||||||
err := newSignal.addToConfig()
|
err := newSignal.AddToConfig()
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
c.JSON(http.StatusOK, gin.H{"signal": newSignal.Signal})
|
c.JSON(http.StatusOK, gin.H{"signal": newSignal.Signal})
|
||||||
}
|
}
|
||||||
|
@ -139,11 +138,14 @@ func addSignal(c *gin.Context) {
|
||||||
// @Router /signals/{signalID} [put]
|
// @Router /signals/{signalID} [put]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func updateSignal(c *gin.Context) {
|
func updateSignal(c *gin.Context) {
|
||||||
ok, oldSignal := checkPermissions(c, database.Delete)
|
ok, oldSignal_r := database.CheckSignalPermissions(c, database.Delete)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldSignal Signal
|
||||||
|
oldSignal.Signal = oldSignal_r
|
||||||
|
|
||||||
var req updateSignalRequest
|
var req updateSignalRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.BadRequestError(c, err.Error())
|
helper.BadRequestError(c, err.Error())
|
||||||
|
@ -181,12 +183,12 @@ func updateSignal(c *gin.Context) {
|
||||||
// @Router /signals/{signalID} [get]
|
// @Router /signals/{signalID} [get]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getSignal(c *gin.Context) {
|
func getSignal(c *gin.Context) {
|
||||||
ok, sig := checkPermissions(c, database.Delete)
|
ok, sig := database.CheckSignalPermissions(c, database.Delete)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"signal": sig.Signal})
|
c.JSON(http.StatusOK, gin.H{"signal": sig})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteSignal godoc
|
// deleteSignal godoc
|
||||||
|
@ -204,11 +206,14 @@ func getSignal(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteSignal(c *gin.Context) {
|
func deleteSignal(c *gin.Context) {
|
||||||
|
|
||||||
ok, sig := checkPermissions(c, database.Delete)
|
ok, sig_r := database.CheckSignalPermissions(c, database.Delete)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sig Signal
|
||||||
|
sig.Signal = sig_r
|
||||||
|
|
||||||
err := sig.delete()
|
err := sig.delete()
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
c.JSON(http.StatusOK, gin.H{"signal": sig.Signal})
|
c.JSON(http.StatusOK, gin.H{"signal": sig.Signal})
|
||||||
|
|
|
@ -23,7 +23,6 @@ package signal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Signal struct {
|
type Signal struct {
|
||||||
|
@ -36,20 +35,20 @@ func (s *Signal) save() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Signal) byID(id uint) error {
|
/*func (s *Signal) byID(id uint) error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
err := db.Find(s, id).Error
|
err := db.Find(s, id).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}*/
|
||||||
|
|
||||||
func (s *Signal) addToConfig() error {
|
func (s *Signal) AddToConfig() error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var err error
|
var err error
|
||||||
var m component_configuration.ComponentConfiguration
|
var m database.ComponentConfiguration
|
||||||
err = m.ByID(s.ConfigID)
|
err = db.Find(&m, s.ConfigID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -92,8 +91,8 @@ func (s *Signal) delete() error {
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var err error
|
var err error
|
||||||
var m component_configuration.ComponentConfiguration
|
var m database.ComponentConfiguration
|
||||||
err = m.ByID(s.ConfigID)
|
err = db.Find(&m, s.ConfigID).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
/** Signal package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
package signal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkPermissions(c *gin.Context, operation database.CRUD) (bool, Signal) {
|
|
||||||
|
|
||||||
var sig Signal
|
|
||||||
|
|
||||||
err := database.ValidateRole(c, database.ModelSignal, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of signal failed): %v", err.Error()))
|
|
||||||
return false, sig
|
|
||||||
}
|
|
||||||
|
|
||||||
signalID, err := helper.GetIDOfElement(c, "signalID", "path", -1)
|
|
||||||
if err != nil {
|
|
||||||
return false, sig
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sig.byID(uint(signalID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, sig
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, _ := component_configuration.CheckPermissions(c, operation, "body", int(sig.ConfigID))
|
|
||||||
if !ok {
|
|
||||||
return false, sig
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, sig
|
|
||||||
}
|
|
|
@ -91,7 +91,7 @@ func newFalse() *bool {
|
||||||
func addScenarioAndICAndConfig() (scenarioID uint, ICID uint, configID uint) {
|
func addScenarioAndICAndConfig() (scenarioID uint, ICID uint, configID uint) {
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, _ := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
token, _ := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
|
|
||||||
// POST $newICA
|
// POST $newICA
|
||||||
newICA := ICRequest{
|
newICA := ICRequest{
|
||||||
|
@ -118,7 +118,7 @@ func addScenarioAndICAndConfig() (scenarioID uint, ICID uint, configID uint) {
|
||||||
newICID, _ := helper.GetResponseID(resp)
|
newICID, _ := helper.GetResponseID(resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, _ = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, _ = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
|
|
||||||
// POST $newScenario
|
// POST $newScenario
|
||||||
newScenario := ScenarioRequest{
|
newScenario := ScenarioRequest{
|
||||||
|
@ -187,7 +187,7 @@ func TestMain(m *testing.M) {
|
||||||
func TestAddSignal(t *testing.T) {
|
func TestAddSignal(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -195,12 +195,12 @@ func TestAddSignal(t *testing.T) {
|
||||||
_, _, configID := addScenarioAndICAndConfig()
|
_, _, configID := addScenarioAndICAndConfig()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
_, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
_, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
newSignal1.ConfigID = configID
|
newSignal1.ConfigID = configID
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST to component config without access
|
// try to POST to component config without access
|
||||||
|
@ -211,7 +211,7 @@ func TestAddSignal(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST a signal with non JSON body
|
// try to POST a signal with non JSON body
|
||||||
|
@ -257,7 +257,7 @@ func TestAddSignal(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Try to Get the newSignal as user B
|
// Try to Get the newSignal as user B
|
||||||
|
@ -271,7 +271,7 @@ func TestAddSignal(t *testing.T) {
|
||||||
func TestUpdateSignal(t *testing.T) {
|
func TestUpdateSignal(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -279,7 +279,7 @@ func TestUpdateSignal(t *testing.T) {
|
||||||
_, _, configID := addScenarioAndICAndConfig()
|
_, _, configID := addScenarioAndICAndConfig()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST signals/ $newSignal
|
// test POST signals/ $newSignal
|
||||||
|
@ -300,7 +300,7 @@ func TestUpdateSignal(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT signal without access
|
// try to PUT signal without access
|
||||||
|
@ -311,7 +311,7 @@ func TestUpdateSignal(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user
|
// authenticate as guest user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to update signal as guest who has access to scenario
|
// try to update signal as guest who has access to scenario
|
||||||
|
@ -322,7 +322,7 @@ func TestUpdateSignal(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT with non JSON body
|
// try to PUT with non JSON body
|
||||||
|
@ -363,7 +363,7 @@ func TestUpdateSignal(t *testing.T) {
|
||||||
func TestDeleteSignal(t *testing.T) {
|
func TestDeleteSignal(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -371,7 +371,7 @@ func TestDeleteSignal(t *testing.T) {
|
||||||
_, _, configID := addScenarioAndICAndConfig()
|
_, _, configID := addScenarioAndICAndConfig()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST signals/ $newSignal
|
// test POST signals/ $newSignal
|
||||||
|
@ -392,7 +392,7 @@ func TestDeleteSignal(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Try to DELETE signal with no access
|
// Try to DELETE signal with no access
|
||||||
|
@ -403,7 +403,7 @@ func TestDeleteSignal(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the input signals returned for component config
|
// Count the number of all the input signals returned for component config
|
||||||
|
@ -449,7 +449,7 @@ func TestDeleteSignal(t *testing.T) {
|
||||||
func TestGetAllInputSignalsOfConfig(t *testing.T) {
|
func TestGetAllInputSignalsOfConfig(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// prepare the content of the DB for testing
|
// prepare the content of the DB for testing
|
||||||
// by adding a scenario and a IC to the DB
|
// by adding a scenario and a IC to the DB
|
||||||
|
@ -457,7 +457,7 @@ func TestGetAllInputSignalsOfConfig(t *testing.T) {
|
||||||
_, _, configID := addScenarioAndICAndConfig()
|
_, _, configID := addScenarioAndICAndConfig()
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the input signals returned for component config
|
// Count the number of all the input signals returned for component config
|
||||||
|
@ -532,7 +532,7 @@ func TestGetAllInputSignalsOfConfig(t *testing.T) {
|
||||||
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal userB who has no access to new scenario
|
// authenticate as normal userB who has no access to new scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to get all input signals
|
// try to get all input signals
|
||||||
|
|
|
@ -35,6 +35,12 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var session *helper.AMQPsession
|
||||||
|
|
||||||
|
func SetAMQPSession(s *helper.AMQPsession) {
|
||||||
|
session = s
|
||||||
|
}
|
||||||
|
|
||||||
type tokenClaims struct {
|
type tokenClaims struct {
|
||||||
UserID uint `json:"id"`
|
UserID uint `json:"id"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
|
@ -68,7 +74,7 @@ func authenticated(c *gin.Context) {
|
||||||
userID, _ := c.Get(database.UserIDCtx)
|
userID, _ := c.Get(database.UserIDCtx)
|
||||||
|
|
||||||
var user User
|
var user User
|
||||||
err := user.ByID(userID.(uint))
|
err := user.byID(userID.(uint))
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -120,7 +126,8 @@ func authenticate(c *gin.Context) {
|
||||||
case "internal":
|
case "internal":
|
||||||
myUser, err = authenticateInternal(c)
|
myUser, err = authenticateInternal(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.BadRequestError(c, err.Error())
|
log.Println("internal auth. failed with error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
case "external":
|
case "external":
|
||||||
var authExternal bool
|
var authExternal bool
|
||||||
|
@ -128,13 +135,16 @@ func authenticate(c *gin.Context) {
|
||||||
if err == nil && authExternal {
|
if err == nil && authExternal {
|
||||||
myUser, err = authenticateExternal(c)
|
myUser, err = authenticateExternal(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.BadRequestError(c, err.Error())
|
log.Println("external auth. failed with error: ", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
helper.BadRequestError(c, "External authentication is not activated")
|
helper.BadRequestError(c, "External authentication is not activated")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
helper.BadRequestError(c, "Invalid authentication mechanism")
|
helper.BadRequestError(c, "Invalid authentication mechanism")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is an active user
|
// Check if this is an active user
|
||||||
|
@ -204,7 +214,7 @@ func authenticateInternal(c *gin.Context) (User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the username in the database
|
// Find the username in the database
|
||||||
err := myUser.ByUsername(credentials.Username)
|
err := myUser.byUsername(credentials.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.UnauthorizedError(c, "Unknown username")
|
helper.UnauthorizedError(c, "Unknown username")
|
||||||
return myUser, err
|
return myUser, err
|
||||||
|
@ -238,7 +248,7 @@ func authenticateExternal(c *gin.Context) (User, error) {
|
||||||
// preferred_username := c.Request.Header.Get("X-Forwarded-Preferred-Username")
|
// preferred_username := c.Request.Header.Get("X-Forwarded-Preferred-Username")
|
||||||
|
|
||||||
// check if user already exists
|
// check if user already exists
|
||||||
err := myUser.ByUsername(username)
|
err := myUser.byUsername(username)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// this is the first login, create new user
|
// this is the first login, create new user
|
||||||
|
@ -277,7 +287,11 @@ func authenticateExternal(c *gin.Context) (User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if groupedScenario.Duplicate {
|
if groupedScenario.Duplicate {
|
||||||
DuplicateScenarioForUser(&so, &myUser.User)
|
|
||||||
|
if err := <-duplicateScenarioForUser(so, &myUser.User, ""); err != nil {
|
||||||
|
return User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
} else { // add user to scenario
|
} else { // add user to scenario
|
||||||
err = db.Model(&so).Association("Users").Append(&(myUser.User)).Error
|
err = db.Model(&so).Association("Users").Append(&(myUser.User)).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -300,257 +314,3 @@ func isAlreadyDuplicated(duplicatedName string) bool {
|
||||||
|
|
||||||
return (len(scenarios) > 0)
|
return (len(scenarios) > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DuplicateScenarioForUser(so *database.Scenario, user *database.User) {
|
|
||||||
go func() {
|
|
||||||
|
|
||||||
// get all component configs of the scenario
|
|
||||||
db := database.GetDB()
|
|
||||||
var configs []database.ComponentConfiguration
|
|
||||||
err := db.Order("ID asc").Model(so).Related(&configs, "ComponentConfigurations").Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Warning: scenario to duplicate (id=%d) has no component configurations", so.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate over component configs to check for ICs to duplicate
|
|
||||||
duplicatedICuuids := make(map[uint]string) // key: original icID; value: UUID of duplicate
|
|
||||||
var externalUUIDs []string // external ICs to wait for
|
|
||||||
for _, config := range configs {
|
|
||||||
icID := config.ICID
|
|
||||||
if duplicatedICuuids[icID] != "" { // this IC was already added
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var ic database.InfrastructureComponent
|
|
||||||
err = db.Find(&ic, icID).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Cannot find IC with id %d in DB, will not duplicate for User %s", icID, user.Username)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new kubernetes simulator OR use existing IC
|
|
||||||
if ic.Category == "simulator" && ic.Type == "kubernetes" {
|
|
||||||
duplicateUUID, err := helper.RequestICcreateAMQPsimpleManager(&ic, ic.Manager, user.Username)
|
|
||||||
duplicatedICuuids[ic.ID] = duplicateUUID
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Duplication of IC (id=%d) unsuccessful, err: %s", icID, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
externalUUIDs = append(externalUUIDs, duplicateUUID)
|
|
||||||
} else { // use existing IC
|
|
||||||
duplicatedICuuids[ic.ID] = ""
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy scenario after all new external ICs are in DB
|
|
||||||
icsToWaitFor := len(externalUUIDs)
|
|
||||||
var duplicatedScenario database.Scenario
|
|
||||||
var timeout = 20 // seconds
|
|
||||||
|
|
||||||
for i := 0; i < timeout; i++ {
|
|
||||||
// duplicate scenario after all duplicated ICs have been found in the DB
|
|
||||||
if icsToWaitFor == 0 {
|
|
||||||
duplicateScenario(so, &duplicatedScenario, duplicatedICuuids, user.Username)
|
|
||||||
|
|
||||||
// associate user to new scenario
|
|
||||||
err = db.Model(&duplicatedScenario).Association("Users").Append(user).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Could not associate User %s to scenario %d", user.Username, duplicatedScenario.ID)
|
|
||||||
}
|
|
||||||
log.Println("Associated user to duplicated scenario")
|
|
||||||
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for new ICs with previously created UUIDs
|
|
||||||
for _, uuid := range externalUUIDs {
|
|
||||||
if uuid == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Printf("Looking for duplicated IC with UUID %s", uuid)
|
|
||||||
var duplicatedIC database.InfrastructureComponent
|
|
||||||
err = db.Find(&duplicatedIC, "UUID = ?", uuid).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error looking up duplicated IC: %s", err)
|
|
||||||
} else {
|
|
||||||
icsToWaitFor--
|
|
||||||
uuid = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Printf("ALERT! Timed out while waiting for IC duplication, scenario not properly duplicated")
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func duplicateComponentConfig(config *database.ComponentConfiguration,
|
|
||||||
duplicateSo *database.Scenario, icIds map[uint]string, userName string, signalMap *map[uint]uint) error {
|
|
||||||
var configDpl database.ComponentConfiguration
|
|
||||||
configDpl.Name = config.Name
|
|
||||||
configDpl.StartParameters = config.StartParameters
|
|
||||||
configDpl.ScenarioID = duplicateSo.ID
|
|
||||||
configDpl.OutputMapping = config.OutputMapping
|
|
||||||
configDpl.InputMapping = config.InputMapping
|
|
||||||
|
|
||||||
db := database.GetDB()
|
|
||||||
if icIds[config.ICID] == "" {
|
|
||||||
configDpl.ICID = config.ICID
|
|
||||||
} else {
|
|
||||||
var duplicatedIC database.InfrastructureComponent
|
|
||||||
err := db.Find(&duplicatedIC, "UUID = ?", icIds[config.ICID]).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
configDpl.ICID = duplicatedIC.ID
|
|
||||||
}
|
|
||||||
err := db.Create(&configDpl).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all signals corresponding to component config
|
|
||||||
var sigs []database.Signal
|
|
||||||
err = db.Order("ID asc").Model(&config).Related(&sigs, "OutputMapping").Error
|
|
||||||
smap := *signalMap
|
|
||||||
for _, signal := range sigs {
|
|
||||||
var sig database.Signal
|
|
||||||
sig.Direction = signal.Direction
|
|
||||||
sig.Index = signal.Index
|
|
||||||
sig.Name = signal.Name + ` ` + userName
|
|
||||||
sig.ScalingFactor = signal.ScalingFactor
|
|
||||||
sig.Unit = signal.Unit
|
|
||||||
sig.ConfigID = configDpl.ID
|
|
||||||
err = db.Create(&sig).Error
|
|
||||||
if err == nil {
|
|
||||||
smap[signal.ID] = sig.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func duplicateScenario(so *database.Scenario, duplicateSo *database.Scenario, icIds map[uint]string, userName string) error {
|
|
||||||
duplicateSo.Name = so.Name + ` ` + userName
|
|
||||||
duplicateSo.StartParameters.RawMessage = so.StartParameters.RawMessage
|
|
||||||
db := database.GetDB()
|
|
||||||
err := db.Create(&duplicateSo).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Could not create duplicate of scenario %d", so.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = duplicateFiles(so, duplicateSo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var configs []database.ComponentConfiguration
|
|
||||||
// map existing signal IDs to duplicated signal IDs for widget duplication
|
|
||||||
signalMap := make(map[uint]uint)
|
|
||||||
err = db.Order("ID asc").Model(so).Related(&configs, "ComponentConfigurations").Error
|
|
||||||
if err == nil {
|
|
||||||
for _, config := range configs {
|
|
||||||
err = duplicateComponentConfig(&config, duplicateSo, icIds, userName, &signalMap)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return duplicateDashboards(so, duplicateSo, signalMap, userName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func duplicateFiles(originalSo *database.Scenario, duplicateSo *database.Scenario) error {
|
|
||||||
db := database.GetDB()
|
|
||||||
var files []database.File
|
|
||||||
err := db.Order("ID asc").Model(originalSo).Related(&files, "Files").Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("error getting files for scenario %d", originalSo.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
var duplicateF database.File
|
|
||||||
duplicateF.Name = file.Name
|
|
||||||
duplicateF.Key = file.Key
|
|
||||||
duplicateF.Type = file.Type
|
|
||||||
duplicateF.Size = file.Size
|
|
||||||
duplicateF.Date = file.Date
|
|
||||||
duplicateF.ScenarioID = duplicateSo.ID
|
|
||||||
duplicateF.FileData = file.FileData
|
|
||||||
duplicateF.ImageHeight = file.ImageHeight
|
|
||||||
duplicateF.ImageWidth = file.ImageWidth
|
|
||||||
err = db.Create(&duplicateF).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Print("error creating duplicate file")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func duplicateDashboards(originalSo *database.Scenario, duplicateSo *database.Scenario,
|
|
||||||
signalMap map[uint]uint, userName string) error {
|
|
||||||
|
|
||||||
db := database.GetDB()
|
|
||||||
var dabs []database.Dashboard
|
|
||||||
err := db.Order("ID asc").Model(originalSo).Related(&dabs, "Dashboards").Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error getting dashboards for scenario %d: %s", originalSo.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dab := range dabs {
|
|
||||||
var duplicateD database.Dashboard
|
|
||||||
duplicateD.Grid = dab.Grid
|
|
||||||
duplicateD.Name = dab.Name + ` ` + userName
|
|
||||||
duplicateD.ScenarioID = duplicateSo.ID
|
|
||||||
duplicateD.Height = dab.Height
|
|
||||||
err = db.Create(&duplicateD).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error creating duplicate dashboard '%s': %s", dab.Name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// add widgets to duplicated dashboards
|
|
||||||
var widgets []database.Widget
|
|
||||||
err = db.Order("ID asc").Model(&dab).Related(&widgets, "Widgets").Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error getting widgets for dashboard %d: %s", dab.ID, err)
|
|
||||||
}
|
|
||||||
for _, widget := range widgets {
|
|
||||||
var duplicateW database.Widget
|
|
||||||
duplicateW.DashboardID = duplicateD.ID
|
|
||||||
duplicateW.CustomProperties = widget.CustomProperties
|
|
||||||
duplicateW.Height = widget.Height
|
|
||||||
duplicateW.Width = widget.Width
|
|
||||||
duplicateW.MinHeight = widget.MinHeight
|
|
||||||
duplicateW.MinWidth = widget.MinWidth
|
|
||||||
duplicateW.Name = widget.Name
|
|
||||||
duplicateW.Type = widget.Type
|
|
||||||
duplicateW.X = widget.X
|
|
||||||
duplicateW.Y = widget.Y
|
|
||||||
|
|
||||||
duplicateW.SignalIDs = []int64{}
|
|
||||||
for _, id := range widget.SignalIDs {
|
|
||||||
duplicateW.SignalIDs = append(duplicateW.SignalIDs, int64(signalMap[uint(id)]))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.Create(&duplicateW).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Print("error creating duplicate widget")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// associate dashboard with simulation
|
|
||||||
err = db.Model(&duplicateD).Association("Widgets").Append(&duplicateW).Error
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error associating duplicate widget and dashboard: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
513
routes/user/scenario_duplication.go
Normal file
513
routes/user/scenario_duplication.go
Normal file
|
@ -0,0 +1,513 @@
|
||||||
|
/** User package, authentication endpoint.
|
||||||
|
*
|
||||||
|
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
||||||
|
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||||
|
* @license GNU General Public License (version 3)
|
||||||
|
*
|
||||||
|
* VILLASweb-backend-go
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************************/
|
||||||
|
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func duplicateScenarioForUser(s database.Scenario, user *database.User, uuidstr string) <-chan error {
|
||||||
|
errs := make(chan error, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
// get all component configs of the scenario
|
||||||
|
db := database.GetDB()
|
||||||
|
var configs []database.ComponentConfiguration
|
||||||
|
err := db.Order("ID asc").Model(s).Related(&configs, "ComponentConfigurations").Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Warning: scenario to duplicate (id=%d) has no component configurations", s.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over component configs to check for ICs to duplicate
|
||||||
|
duplicatedICuuids := make(map[uint]string) // key: original icID; value: UUID of duplicate
|
||||||
|
var externalUUIDs []string // external ICs to wait for
|
||||||
|
for _, config := range configs {
|
||||||
|
icID := config.ICID
|
||||||
|
if duplicatedICuuids[icID] != "" { // this IC was already added
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var ic database.InfrastructureComponent
|
||||||
|
err = db.Find(&ic, icID).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Cannot find IC with id %d in DB, will not duplicate for User %s: %s", icID, user.Username, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new kubernetes simulator OR use existing IC
|
||||||
|
if ic.Category == "simulator" && ic.Type == "kubernetes" {
|
||||||
|
duplicateUUID, err := duplicateIC(ic, user.Username, uuidstr)
|
||||||
|
if err != nil {
|
||||||
|
errs <- fmt.Errorf("duplication of IC (id=%d) unsuccessful, err: %s", icID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedICuuids[ic.ID] = duplicateUUID
|
||||||
|
externalUUIDs = append(externalUUIDs, duplicateUUID)
|
||||||
|
} else { // use existing IC
|
||||||
|
duplicatedICuuids[ic.ID] = ""
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy scenario after all new external ICs are in DB
|
||||||
|
icsToWaitFor := len(externalUUIDs)
|
||||||
|
var timeout = 20 // seconds
|
||||||
|
|
||||||
|
for i := 0; i < timeout; i++ {
|
||||||
|
// duplicate scenario after all duplicated ICs have been found in the DB
|
||||||
|
if icsToWaitFor == 0 {
|
||||||
|
err := duplicateScenario(s, duplicatedICuuids, user)
|
||||||
|
if err != nil {
|
||||||
|
errs <- fmt.Errorf("duplicate scenario %v fails with error %v", s.Name, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
close(errs)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for new ICs with previously created UUIDs
|
||||||
|
for _, uuid_r := range externalUUIDs {
|
||||||
|
if uuid_r == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("Looking for duplicated IC with UUID %s", uuid_r)
|
||||||
|
var duplicatedIC database.InfrastructureComponent
|
||||||
|
err = db.Find(&duplicatedIC, "UUID = ?", uuid_r).Error
|
||||||
|
if err != nil {
|
||||||
|
errs <- fmt.Errorf("Error looking up duplicated IC: %s", err)
|
||||||
|
} else {
|
||||||
|
icsToWaitFor--
|
||||||
|
uuid_r = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errs <- fmt.Errorf("ALERT! Timed out while waiting for IC duplication, scenario not properly duplicated")
|
||||||
|
close(errs)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func duplicateScenario(s database.Scenario, icIds map[uint]string, user *database.User) error {
|
||||||
|
|
||||||
|
db := database.GetDB()
|
||||||
|
|
||||||
|
var duplicateSo database.Scenario
|
||||||
|
duplicateSo.Name = s.Name + ` ` + user.Username
|
||||||
|
duplicateSo.StartParameters.RawMessage = s.StartParameters.RawMessage
|
||||||
|
|
||||||
|
err := db.Create(&duplicateSo).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not create duplicate of scenario %d", s.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// associate user to new scenario
|
||||||
|
err = db.Model(&duplicateSo).Association("Users").Append(user).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not associate User %s to scenario %d", user.Username, duplicateSo.ID)
|
||||||
|
}
|
||||||
|
log.Println("Associated user to duplicated scenario")
|
||||||
|
|
||||||
|
// duplicate files
|
||||||
|
var files []database.File
|
||||||
|
err = db.Order("ID asc").Model(s).Related(&files, "Files").Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error getting files for scenario %d", s.ID)
|
||||||
|
}
|
||||||
|
for _, f := range files {
|
||||||
|
err = duplicateFile(f, duplicateSo.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error creating duplicate file %d: %s", f.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var configs []database.ComponentConfiguration
|
||||||
|
// map existing signal IDs to duplicated signal IDs for widget duplication
|
||||||
|
signalMap := make(map[uint]uint)
|
||||||
|
err = db.Order("ID asc").Model(s).Related(&configs, "ComponentConfigurations").Error
|
||||||
|
if err == nil {
|
||||||
|
for _, c := range configs {
|
||||||
|
err = duplicateComponentConfig(c, duplicateSo.ID, icIds, &signalMap)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error duplicating component config %d: %s", c.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var dabs []database.Dashboard
|
||||||
|
err = db.Order("ID asc").Model(s).Related(&dabs, "Dashboards").Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting dashboards for scenario %d: %s", s.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dab := range dabs {
|
||||||
|
err = duplicateDashboard(dab, duplicateSo.ID, signalMap)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error duplicating dashboard %d: %s", dab.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func duplicateFile(f database.File, scenarioID uint) error {
|
||||||
|
|
||||||
|
var dup database.File
|
||||||
|
dup.Name = f.Name
|
||||||
|
dup.Key = f.Key
|
||||||
|
dup.Type = f.Type
|
||||||
|
dup.Size = f.Size
|
||||||
|
dup.Date = f.Date
|
||||||
|
dup.ScenarioID = scenarioID
|
||||||
|
dup.FileData = f.FileData
|
||||||
|
dup.ImageHeight = f.ImageHeight
|
||||||
|
dup.ImageWidth = f.ImageWidth
|
||||||
|
|
||||||
|
// file duplicate will point to the same data blob in the DB (SQL or postgres)
|
||||||
|
|
||||||
|
// Add duplicate File object with parameters to DB
|
||||||
|
db := database.GetDB()
|
||||||
|
err := db.Create(&dup).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create association of duplicate file to scenario ID of duplicate file
|
||||||
|
|
||||||
|
var so database.Scenario
|
||||||
|
err = db.Find(&so, scenarioID).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Model(&so).Association("Files").Append(&dup).Error
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func duplicateComponentConfig(m database.ComponentConfiguration, scenarioID uint, icIds map[uint]string, signalMap *map[uint]uint) error {
|
||||||
|
|
||||||
|
db := database.GetDB()
|
||||||
|
|
||||||
|
var dup database.ComponentConfiguration
|
||||||
|
dup.Name = m.Name
|
||||||
|
dup.StartParameters = m.StartParameters
|
||||||
|
dup.ScenarioID = scenarioID
|
||||||
|
|
||||||
|
if icIds[m.ICID] == "" {
|
||||||
|
dup.ICID = m.ICID
|
||||||
|
} else {
|
||||||
|
var duplicatedIC database.InfrastructureComponent
|
||||||
|
err := db.Find(&duplicatedIC, "UUID = ?", icIds[m.ICID]).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dup.ICID = duplicatedIC.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// save duplicate to DB and create associations with IC and scenario
|
||||||
|
var so database.Scenario
|
||||||
|
err := db.Find(&so, scenarioID).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// save component configuration to DB
|
||||||
|
err = db.Create(&dup).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// associate IC with component configuration
|
||||||
|
var ic database.InfrastructureComponent
|
||||||
|
err = db.Find(&ic, dup.ICID).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = db.Model(&ic).Association("ComponentConfigurations").Append(&dup).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// associate component configuration with scenario
|
||||||
|
err = db.Model(&so).Association("ComponentConfigurations").Append(&dup).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// duplication of signals
|
||||||
|
var sigs []database.Signal
|
||||||
|
err = db.Order("ID asc").Model(&m).Related(&sigs, "OutputMapping").Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
smap := *signalMap
|
||||||
|
for _, s := range sigs {
|
||||||
|
var sigDup database.Signal
|
||||||
|
sigDup.Direction = s.Direction
|
||||||
|
sigDup.Index = s.Index
|
||||||
|
sigDup.Name = s.Name // + ` ` + userName
|
||||||
|
sigDup.ScalingFactor = s.ScalingFactor
|
||||||
|
sigDup.Unit = s.Unit
|
||||||
|
sigDup.ConfigID = dup.ID
|
||||||
|
|
||||||
|
// save signal to DB
|
||||||
|
err = db.Create(&sigDup).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// associate signal with component configuration in correct direction
|
||||||
|
if sigDup.Direction == "in" {
|
||||||
|
err = db.Model(&dup).Association("InputMapping").Append(&sigDup).Error
|
||||||
|
} else {
|
||||||
|
err = db.Model(&dup).Association("OutputMapping").Append(&sigDup).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
smap[s.ID] = sigDup.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func duplicateDashboard(d database.Dashboard, scenarioID uint, signalMap map[uint]uint) error {
|
||||||
|
|
||||||
|
var duplicateD database.Dashboard
|
||||||
|
duplicateD.Grid = d.Grid
|
||||||
|
duplicateD.Name = d.Name
|
||||||
|
duplicateD.ScenarioID = scenarioID
|
||||||
|
duplicateD.Height = d.Height
|
||||||
|
|
||||||
|
db := database.GetDB()
|
||||||
|
var so database.Scenario
|
||||||
|
err := db.Find(&so, duplicateD.ScenarioID).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// save dashboard to DB
|
||||||
|
err = db.Create(&duplicateD).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// associate dashboard with scenario
|
||||||
|
err = db.Model(&so).Association("Dashboards").Append(&duplicateD).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add widgets to duplicated dashboard
|
||||||
|
var widgets []database.Widget
|
||||||
|
err = db.Order("ID asc").Model(d).Related(&widgets, "Widgets").Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting widgets for dashboard %d: %s", d.ID, err)
|
||||||
|
}
|
||||||
|
for _, w := range widgets {
|
||||||
|
|
||||||
|
err = duplicateWidget(w, duplicateD.ID, signalMap)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error creating duplicate for widget %d: %s", w.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func duplicateWidget(w database.Widget, dashboardID uint, signalMap map[uint]uint) error {
|
||||||
|
var duplicateW database.Widget
|
||||||
|
duplicateW.DashboardID = dashboardID
|
||||||
|
duplicateW.CustomProperties = w.CustomProperties
|
||||||
|
duplicateW.Height = w.Height
|
||||||
|
duplicateW.Width = w.Width
|
||||||
|
duplicateW.MinHeight = w.MinHeight
|
||||||
|
duplicateW.MinWidth = w.MinWidth
|
||||||
|
duplicateW.Name = w.Name
|
||||||
|
duplicateW.Type = w.Type
|
||||||
|
duplicateW.X = w.X
|
||||||
|
duplicateW.Y = w.Y
|
||||||
|
duplicateW.Z = w.Z
|
||||||
|
|
||||||
|
duplicateW.SignalIDs = []int64{}
|
||||||
|
for _, id := range w.SignalIDs {
|
||||||
|
duplicateW.SignalIDs = append(duplicateW.SignalIDs, int64(signalMap[uint(id)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
db := database.GetDB()
|
||||||
|
var dab database.Dashboard
|
||||||
|
err := db.Find(&dab, duplicateW.DashboardID).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// save widget to DB
|
||||||
|
err = db.Create(&duplicateW).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// associate widget with dashboard
|
||||||
|
err = db.Model(&dab).Association("Widgets").Append(&duplicateW).Error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TemplateSpec struct {
|
||||||
|
Containers []Container `json:"containers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobTemplate struct {
|
||||||
|
Spec TemplateSpec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobSpec struct {
|
||||||
|
Active string `json:"activeDeadlineSeconds"`
|
||||||
|
Template JobTemplate `json:"template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JobMetaData struct {
|
||||||
|
JobName string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KubernetesJob struct {
|
||||||
|
Spec JobSpec `json:"spec"`
|
||||||
|
MetaData JobMetaData `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ICPropertiesKubernetesJob struct {
|
||||||
|
Job KubernetesJob `json:"job"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ICStatus struct {
|
||||||
|
State string `json:"state"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Uptime float64 `json:"uptime"`
|
||||||
|
Result string `json:"result"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
ManagedBy string `json:"managed_by"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ICUpdateKubernetesJob struct {
|
||||||
|
Properties ICPropertiesKubernetesJob `json:"properties"`
|
||||||
|
Status ICStatus `json:"status"`
|
||||||
|
Schema json.RawMessage `json:"schema"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func duplicateIC(ic database.InfrastructureComponent, userName string, uuidstr string) (string, error) {
|
||||||
|
|
||||||
|
//WARNING: this function only works with the kubernetes-simple manager of VILLAScontroller
|
||||||
|
if ic.Category != "simulator" || ic.Type != "kubernetes" {
|
||||||
|
return "", fmt.Errorf("IC to duplicate is not a kubernetes simulator (%s %s)", ic.Type, ic.Category)
|
||||||
|
}
|
||||||
|
|
||||||
|
newUUID := uuid.New().String()
|
||||||
|
|
||||||
|
var lastUpdate ICUpdateKubernetesJob
|
||||||
|
err := json.Unmarshal(ic.StatusUpdateRaw.RawMessage, &lastUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return newUUID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if uuidstr != "" {
|
||||||
|
newUUID = "4854af30-325f-44a5-ad59-b67b2597de68"
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := `{"name": "` + lastUpdate.Properties.Name + ` ` + userName + `",` +
|
||||||
|
`"location": "` + lastUpdate.Properties.Location + `",` +
|
||||||
|
`"category": "` + lastUpdate.Properties.Category + `",` +
|
||||||
|
`"type": "` + lastUpdate.Properties.Type + `",` +
|
||||||
|
`"uuid": "` + newUUID + `",` +
|
||||||
|
`"jobname": "` + lastUpdate.Properties.Job.MetaData.JobName + `-` + userName + `",` +
|
||||||
|
`"activeDeadlineSeconds": "` + lastUpdate.Properties.Job.Spec.Active + `",` +
|
||||||
|
`"containername": "` + lastUpdate.Properties.Job.Spec.Template.Spec.Containers[0].Name + `-` + userName + `",` +
|
||||||
|
`"image": "` + lastUpdate.Properties.Job.Spec.Template.Spec.Containers[0].Image + `",` +
|
||||||
|
`"uuid": "` + newUUID + `"}`
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Act string `json:"action"`
|
||||||
|
When int64 `json:"when"`
|
||||||
|
Parameters json.RawMessage `json:"parameters,omitempty"`
|
||||||
|
Model json.RawMessage `json:"model,omitempty"`
|
||||||
|
Results json.RawMessage `json:"results,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
actionCreate := Action{
|
||||||
|
Act: "create",
|
||||||
|
When: time.Now().Unix(),
|
||||||
|
Parameters: json.RawMessage(msg),
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := json.Marshal(actionCreate)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if session != nil {
|
||||||
|
if session.IsReady {
|
||||||
|
err = session.Send(payload, ic.Manager)
|
||||||
|
return newUUID, err
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("could not send IC create action, AMQP session is not ready")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("could not send IC create action, AMQP session is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -178,7 +178,7 @@ func updateUser(c *gin.Context) {
|
||||||
|
|
||||||
// Find the user
|
// Find the user
|
||||||
var oldUser User
|
var oldUser User
|
||||||
err = oldUser.ByID(uint(toBeUpdatedID))
|
err = oldUser.byID(uint(toBeUpdatedID))
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,7 @@ func getUser(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var user User
|
var user User
|
||||||
err = user.ByID(uint(id))
|
err = user.byID(uint(id))
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
c.JSON(http.StatusOK, gin.H{"user": user.User})
|
c.JSON(http.StatusOK, gin.H{"user": user.User})
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ func deleteUser(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the user exist
|
// Check that the user exist
|
||||||
err = user.ByID(uint(id))
|
err = user.byID(uint(id))
|
||||||
if helper.DBError(c, err) {
|
if helper.DBError(c, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ func NewUser(username, password, mail, role string, active bool) (User, error) {
|
||||||
var newUser User
|
var newUser User
|
||||||
|
|
||||||
// Check that the username is NOT taken
|
// Check that the username is NOT taken
|
||||||
err := newUser.ByUsername(username)
|
err := newUser.byUsername(username)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return newUser, &UsernameAlreadyTaken{Username: username}
|
return newUser, &UsernameAlreadyTaken{Username: username}
|
||||||
}
|
}
|
||||||
|
@ -83,13 +83,13 @@ func (u *User) remove() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) ByUsername(username string) error {
|
func (u *User) byUsername(username string) error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
err := db.Find(u, "Username = ?", username).Error
|
err := db.Find(u, "Username = ?", username).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) ByID(id uint) error {
|
func (u *User) byID(id uint) error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
err := db.Find(u, id).Error
|
err := db.Find(u, id).Error
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -41,7 +41,7 @@ func claimsToContext(c *gin.Context, claims jwt.MapClaims) error {
|
||||||
|
|
||||||
var user User
|
var user User
|
||||||
|
|
||||||
err := user.ByID(uint(userID))
|
err := user.byID(uint(userID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,19 +25,33 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/jinzhu/gorm/dialects/postgres"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
|
|
||||||
|
component_configuration "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/file"
|
||||||
|
infrastructure_component "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/signal"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
var router *gin.Engine
|
var router *gin.Engine
|
||||||
|
@ -69,6 +83,14 @@ func TestMain(m *testing.M) {
|
||||||
api.Use(Authentication())
|
api.Use(Authentication())
|
||||||
RegisterUserEndpoints(api.Group("/users"))
|
RegisterUserEndpoints(api.Group("/users"))
|
||||||
|
|
||||||
|
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||||
|
infrastructure_component.RegisterICEndpoints(api.Group("/ic"))
|
||||||
|
component_configuration.RegisterComponentConfigurationEndpoints(api.Group("/configs"))
|
||||||
|
signal.RegisterSignalEndpoints(api.Group("/signals"))
|
||||||
|
dashboard.RegisterDashboardEndpoints(api.Group("/dashboards"))
|
||||||
|
file.RegisterFileEndpoints(api.Group("/files"))
|
||||||
|
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
|
||||||
|
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +110,7 @@ func TestAuthenticate(t *testing.T) {
|
||||||
router.ServeHTTP(w1, req)
|
router.ServeHTTP(w1, req)
|
||||||
assert.Equalf(t, 401, w1.Code, "Response body: \n%v\n", w1.Body)
|
assert.Equalf(t, 401, w1.Code, "Response body: \n%v\n", w1.Body)
|
||||||
|
|
||||||
malformedCredentials := helper.Credentials{
|
malformedCredentials := database.Credentials{
|
||||||
Username: "TEST1",
|
Username: "TEST1",
|
||||||
}
|
}
|
||||||
// try to authenticate with malformed credentials
|
// try to authenticate with malformed credentials
|
||||||
|
@ -126,7 +148,7 @@ func TestAuthenticate(t *testing.T) {
|
||||||
assert.Equal(t, 401, w4.Code, w4.Body)
|
assert.Equal(t, 401, w4.Code, w4.Body)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
_, err = helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
_, err = helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -140,7 +162,7 @@ func TestUserGroups(t *testing.T) {
|
||||||
role := "User"
|
role := "User"
|
||||||
userGroups := strings.Split("testGroup1,testGroup2", ",")
|
userGroups := strings.Split("testGroup1,testGroup2", ",")
|
||||||
|
|
||||||
err := myUser.ByUsername(username)
|
err := myUser.byUsername(username)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
myUser, err = NewUser(username, "", email, role, true)
|
myUser, err = NewUser(username, "", email, role, true)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -176,7 +198,7 @@ func TestAuthenticateQueryToken(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
@ -197,7 +219,7 @@ func TestAddGetUser(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST with non JSON body
|
// try to POST with non JSON body
|
||||||
|
@ -321,7 +343,7 @@ func TestUsersNotAllowedActions(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Add a user
|
// Add a user
|
||||||
|
@ -380,7 +402,7 @@ func TestGetAllUsers(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// get the length of the GET all users response
|
// get the length of the GET all users response
|
||||||
|
@ -407,7 +429,7 @@ func TestGetAllUsers(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, finalNumber, initialNumber+1)
|
assert.Equal(t, finalNumber, initialNumber+1)
|
||||||
|
|
||||||
newUserCredentials := helper.Credentials{
|
newUserCredentials := database.Credentials{
|
||||||
Username: newUser.Username,
|
Username: newUser.Username,
|
||||||
Password: newUser.Password,
|
Password: newUser.Password,
|
||||||
}
|
}
|
||||||
|
@ -433,7 +455,7 @@ func TestModifyAddedUserAsUser(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Add a user that will modify itself
|
// Add a user that will modify itself
|
||||||
|
@ -588,7 +610,7 @@ func TestInvalidUserUpdate(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Add a user
|
// Add a user
|
||||||
|
@ -660,7 +682,7 @@ func TestModifyAddedUserAsAdmin(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Add a user
|
// Add a user
|
||||||
|
@ -746,7 +768,7 @@ func TestModifyAddedUserAsAdmin(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err = helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err = helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// modify newUser's Active status
|
// modify newUser's Active status
|
||||||
|
@ -777,7 +799,7 @@ func TestDeleteUser(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as admin
|
// authenticate as admin
|
||||||
token, err := helper.AuthenticateForTest(router, helper.Credentials{Username: "admin", Password: adminpw})
|
token, err := helper.AuthenticateForTest(router, database.Credentials{Username: "admin", Password: adminpw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Add a user
|
// Add a user
|
||||||
|
@ -827,3 +849,492 @@ func TestDeleteUser(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, finalNumber, initialNumber-1)
|
assert.Equal(t, finalNumber, initialNumber-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDuplicateScenarioForUser(t *testing.T) {
|
||||||
|
database.DropTables()
|
||||||
|
database.MigrateModels()
|
||||||
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
|
// connect AMQP client
|
||||||
|
// Make sure that AMQP_HOST, AMQP_USER, AMQP_PASS are set
|
||||||
|
host, _ := configuration.GlobalConfig.String("amqp.host")
|
||||||
|
usr, _ := configuration.GlobalConfig.String("amqp.user")
|
||||||
|
pass, _ := configuration.GlobalConfig.String("amqp.pass")
|
||||||
|
amqpURI := "amqp://" + usr + ":" + pass + "@" + host
|
||||||
|
|
||||||
|
// AMQP Connection startup is tested here
|
||||||
|
// Not repeated in other tests because it is only needed once
|
||||||
|
session = helper.NewAMQPSession("villas-test-session", amqpURI, "villas", infrastructure_component.ProcessMessage)
|
||||||
|
SetAMQPSession(session)
|
||||||
|
infrastructure_component.SetAMQPSession(session)
|
||||||
|
|
||||||
|
// authenticate as admin (needed to create original IC)
|
||||||
|
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
/*** Create original scenario and entities ***/
|
||||||
|
scenarioID := addScenario(token)
|
||||||
|
var originalSo database.Scenario
|
||||||
|
db := database.GetDB()
|
||||||
|
err = db.Find(&originalSo, scenarioID).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// add file to scenario
|
||||||
|
fileID, err := addFile(scenarioID, token)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, 99, fileID)
|
||||||
|
|
||||||
|
// add IC
|
||||||
|
err = addIC(session, token)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// add component config
|
||||||
|
err = addComponentConfig(scenarioID, token)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// add signals
|
||||||
|
err = addSignals(token)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// add dashboards to scenario
|
||||||
|
err = addTwoDashboards(scenarioID, token)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var dashboards []database.Dashboard
|
||||||
|
err = db.Find(&dashboards).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(dashboards))
|
||||||
|
|
||||||
|
// add widgets
|
||||||
|
dashboardID_forAddingWidget := uint(1)
|
||||||
|
err = addWidget(dashboardID_forAddingWidget, token)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
/*** Duplicate scenario for new user ***/
|
||||||
|
username := "Schnittlauch"
|
||||||
|
myUser, err := NewUser(username, "", "schnitti@lauch.de", "User", true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// create fake IC duplicate (would normally be created by villas-controller)
|
||||||
|
uuidDup := "4854af30-325f-44a5-ad59-b67b2597de68"
|
||||||
|
err = addFakeIC(uuidDup, session, token)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
if err := <-duplicateScenarioForUser(originalSo, &myUser.User, uuidDup); err != nil {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Check duplicated scenario for correctness ***/
|
||||||
|
var dplScenarios []database.Scenario
|
||||||
|
err = db.Find(&dplScenarios, "name = ?", originalSo.Name+" "+username).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 1, len(dplScenarios))
|
||||||
|
assert.Equal(t, originalSo.StartParameters, dplScenarios[0].StartParameters)
|
||||||
|
|
||||||
|
// compare original and duplicated file
|
||||||
|
var files []database.File
|
||||||
|
err = db.Find(&files).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(files))
|
||||||
|
assert.Equal(t, files[0].Name, files[1].Name)
|
||||||
|
assert.Equal(t, files[0].FileData, files[1].FileData)
|
||||||
|
assert.Equal(t, files[0].ImageHeight, files[1].ImageHeight)
|
||||||
|
assert.Equal(t, files[0].ImageWidth, files[1].ImageWidth)
|
||||||
|
assert.Equal(t, files[0].Size, files[1].Size)
|
||||||
|
assert.NotEqual(t, files[0].ScenarioID, files[1].ScenarioID)
|
||||||
|
assert.NotEqual(t, files[0].ID, files[1].ID)
|
||||||
|
|
||||||
|
// compare original and duplicated component config
|
||||||
|
var configs []database.ComponentConfiguration
|
||||||
|
err = db.Find(&configs).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(configs))
|
||||||
|
assert.Equal(t, configs[0].Name, configs[1].Name)
|
||||||
|
assert.Equal(t, configs[0].FileIDs, configs[1].FileIDs)
|
||||||
|
assert.Equal(t, configs[0].InputMapping, configs[1].InputMapping)
|
||||||
|
assert.Equal(t, configs[0].OutputMapping, configs[1].OutputMapping)
|
||||||
|
assert.Equal(t, configs[0].StartParameters, configs[1].StartParameters)
|
||||||
|
assert.NotEqual(t, configs[0].ScenarioID, configs[1].ScenarioID)
|
||||||
|
assert.NotEqual(t, configs[0].ICID, configs[1].ICID)
|
||||||
|
assert.NotEqual(t, configs[0].ID, configs[1].ID)
|
||||||
|
|
||||||
|
// compare original and duplicated signals
|
||||||
|
var signals []database.Signal
|
||||||
|
err = db.Order("created_at asc").Find(&signals).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 4, len(signals))
|
||||||
|
assert.Equal(t, signals[0].ConfigID, signals[1].ConfigID)
|
||||||
|
assert.Equal(t, signals[2].ConfigID, signals[3].ConfigID)
|
||||||
|
|
||||||
|
assert.Equal(t, signals[0].Name, signals[2].Name)
|
||||||
|
assert.Equal(t, signals[0].Unit, signals[2].Unit)
|
||||||
|
assert.Equal(t, signals[0].ScalingFactor, signals[2].ScalingFactor)
|
||||||
|
assert.Equal(t, signals[0].Direction, signals[2].Direction)
|
||||||
|
assert.Equal(t, signals[0].Index, signals[2].Index)
|
||||||
|
assert.NotEqual(t, signals[0].ConfigID, signals[2].ConfigID)
|
||||||
|
assert.NotEqual(t, signals[0].ID, signals[2].ID)
|
||||||
|
|
||||||
|
assert.Equal(t, signals[1].Name, signals[3].Name)
|
||||||
|
assert.Equal(t, signals[1].Unit, signals[3].Unit)
|
||||||
|
assert.Equal(t, signals[1].ScalingFactor, signals[3].ScalingFactor)
|
||||||
|
assert.Equal(t, signals[1].Direction, signals[3].Direction)
|
||||||
|
assert.Equal(t, signals[1].Index, signals[3].Index)
|
||||||
|
assert.NotEqual(t, signals[1].ConfigID, signals[3].ConfigID)
|
||||||
|
assert.NotEqual(t, signals[1].ID, signals[3].ID)
|
||||||
|
|
||||||
|
// compare original and duplicated infrastructure component
|
||||||
|
var ics []database.InfrastructureComponent
|
||||||
|
err = db.Find(&ics).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(ics))
|
||||||
|
assert.Equal(t, ics[0].Name, ics[1].Name)
|
||||||
|
assert.Equal(t, ics[0].Category, ics[1].Category)
|
||||||
|
assert.Equal(t, ics[0].Type, ics[1].Type)
|
||||||
|
assert.Equal(t, ics[0].CreateParameterSchema, ics[1].CreateParameterSchema)
|
||||||
|
assert.Equal(t, ics[0].StartParameterSchema, ics[1].StartParameterSchema)
|
||||||
|
assert.Equal(t, ics[0].Manager, ics[1].Manager)
|
||||||
|
assert.NotEqual(t, ics[0].UUID, ics[1].UUID)
|
||||||
|
assert.NotEqual(t, ics[0].ID, ics[1].ID)
|
||||||
|
|
||||||
|
// compare original and duplicated dashboards
|
||||||
|
err = db.Order("created_at asc").Find(&dashboards).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 4, len(dashboards))
|
||||||
|
assert.Equal(t, dashboards[0].Name, dashboards[2].Name)
|
||||||
|
assert.Equal(t, dashboards[0].Grid, dashboards[2].Grid)
|
||||||
|
assert.Equal(t, dashboards[0].Height, dashboards[2].Height)
|
||||||
|
assert.NotEqual(t, dashboards[0].ScenarioID, dashboards[2].ScenarioID)
|
||||||
|
assert.NotEqual(t, dashboards[0].ID, dashboards[2].ID)
|
||||||
|
|
||||||
|
assert.Equal(t, dashboards[1].Name, dashboards[3].Name)
|
||||||
|
assert.Equal(t, dashboards[1].Grid, dashboards[3].Grid)
|
||||||
|
assert.Equal(t, dashboards[1].Height, dashboards[3].Height)
|
||||||
|
assert.NotEqual(t, dashboards[1].ScenarioID, dashboards[3].ScenarioID)
|
||||||
|
assert.NotEqual(t, dashboards[1].ID, dashboards[3].ID)
|
||||||
|
|
||||||
|
// compare original and duplicated widget
|
||||||
|
var widgets []database.Widget
|
||||||
|
err = db.Find(&widgets).Error
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(widgets))
|
||||||
|
assert.Equal(t, widgets[0].Name, widgets[1].Name)
|
||||||
|
assert.Equal(t, widgets[0].CustomProperties, widgets[1].CustomProperties)
|
||||||
|
assert.Equal(t, widgets[0].MinHeight, widgets[1].MinHeight)
|
||||||
|
assert.Equal(t, widgets[0].MinWidth, widgets[1].MinWidth)
|
||||||
|
assert.NotEqual(t, widgets[0].DashboardID, widgets[1].DashboardID)
|
||||||
|
assert.NotEqual(t, widgets[0].ID, widgets[1].ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addScenario(token string) (scenarioID uint) {
|
||||||
|
type ScenarioRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
newScenario := ScenarioRequest{
|
||||||
|
Name: "Scenario1",
|
||||||
|
StartParameters: postgres.Jsonb{json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`)},
|
||||||
|
}
|
||||||
|
_, resp, err := helper.TestEndpoint(router, token,
|
||||||
|
"/api/v2/scenarios", "POST", helper.KeyModels{"scenario": newScenario})
|
||||||
|
if err != nil {
|
||||||
|
log.Panic("The following error happend on POSTing a scenario: ", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read newScenario's ID from the response
|
||||||
|
newScenarioID, _ := helper.GetResponseID(resp)
|
||||||
|
|
||||||
|
// add the guest user to the new scenario
|
||||||
|
_, _, _ = helper.TestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/v2/scenarios/%v/user?username=User_A", newScenarioID), "PUT", nil)
|
||||||
|
|
||||||
|
return uint(newScenarioID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFile(scenarioID uint, token string) (uint, error) {
|
||||||
|
c1 := []byte(`<?xml version="1.0"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="400" height="400">
|
||||||
|
<circle cx="100" cy="100" r="50" stroke="black"
|
||||||
|
stroke-width="5" fill="red" />
|
||||||
|
</svg>`)
|
||||||
|
err := ioutil.WriteFile("circle.svg", c1, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return 99, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// test POST files
|
||||||
|
bodyBuf := &bytes.Buffer{}
|
||||||
|
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||||
|
fileWriter, err := bodyWriter.CreateFormFile("file", "circle.svg")
|
||||||
|
if err != nil {
|
||||||
|
return 99, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// open file handle
|
||||||
|
fh, err := os.Open("circle.svg")
|
||||||
|
if err != nil {
|
||||||
|
return 99, err
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
// io copy
|
||||||
|
_, err = io.Copy(fileWriter, fh)
|
||||||
|
if err != nil {
|
||||||
|
return 99, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := bodyWriter.FormDataContentType()
|
||||||
|
bodyWriter.Close()
|
||||||
|
|
||||||
|
// Create the request
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req, err := http.NewRequest("POST", fmt.Sprintf("/api/v2/files?scenarioID=%v", scenarioID), bodyBuf)
|
||||||
|
if err != nil {
|
||||||
|
return 99, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
req.Header.Add("Authorization", "Bearer "+token)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
newFileID, err := helper.GetResponseID(w.Body)
|
||||||
|
if err != nil {
|
||||||
|
return 99, err
|
||||||
|
}
|
||||||
|
return uint(newFileID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTwoDashboards(scenarioID uint, token string) error {
|
||||||
|
type DashboardRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Grid int `json:"grid,omitempty"`
|
||||||
|
Height int `json:"height,omitempty"`
|
||||||
|
ScenarioID uint `json:"scenarioID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
newDashboardA := DashboardRequest{
|
||||||
|
Name: "Dashboard_A",
|
||||||
|
Grid: 15,
|
||||||
|
Height: 200,
|
||||||
|
ScenarioID: scenarioID,
|
||||||
|
}
|
||||||
|
|
||||||
|
newDashboardB := DashboardRequest{
|
||||||
|
Name: "Dashboard_B",
|
||||||
|
Grid: 35,
|
||||||
|
Height: 555,
|
||||||
|
ScenarioID: scenarioID,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := helper.TestEndpoint(router, token,
|
||||||
|
"/api/v2/dashboards", "POST", helper.KeyModels{"dashboard": newDashboardA})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = helper.TestEndpoint(router, token,
|
||||||
|
"/api/v2/dashboards", "POST", helper.KeyModels{"dashboard": newDashboardB})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addWidget(dashboardID uint, token string) error {
|
||||||
|
type WidgetRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Width uint `json:"width,omitempty"`
|
||||||
|
Height uint `json:"height,omitempty"`
|
||||||
|
MinWidth uint `json:"minWidth,omitempty"`
|
||||||
|
MinHeight uint `json:"minHeight,omitempty"`
|
||||||
|
X int `json:"x,omitempty"`
|
||||||
|
Y int `json:"y,omitempty"`
|
||||||
|
Z int `json:"z,omitempty"`
|
||||||
|
DashboardID uint `json:"dashboardID,omitempty"`
|
||||||
|
IsLocked bool `json:"isLocked,omitempty"`
|
||||||
|
CustomProperties postgres.Jsonb `json:"customProperties,omitempty"`
|
||||||
|
SignalIDs []int64 `json:"signalIDs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
newWidget := WidgetRequest{
|
||||||
|
Name: "My label",
|
||||||
|
Type: "Label",
|
||||||
|
Width: 100,
|
||||||
|
Height: 50,
|
||||||
|
MinWidth: 40,
|
||||||
|
MinHeight: 80,
|
||||||
|
X: 10,
|
||||||
|
Y: 10,
|
||||||
|
Z: 200,
|
||||||
|
IsLocked: false,
|
||||||
|
CustomProperties: postgres.Jsonb{RawMessage: json.RawMessage(`{"textSize" : "20", "fontColor" : "#4287f5", "fontColor_opacity": 1}`)},
|
||||||
|
SignalIDs: []int64{},
|
||||||
|
}
|
||||||
|
|
||||||
|
newWidget.DashboardID = dashboardID
|
||||||
|
|
||||||
|
_, _, err := helper.TestEndpoint(router, token,
|
||||||
|
"/api/v2/widgets", "POST", helper.KeyModels{"widget": newWidget})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTrue() *bool {
|
||||||
|
b := true
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
|
func addIC(session *helper.AMQPsession, token string) error {
|
||||||
|
type ICRequest struct {
|
||||||
|
UUID string `json:"uuid,omitempty"`
|
||||||
|
WebsocketURL string `json:"websocketurl,omitempty"`
|
||||||
|
APIURL string `json:"apiurl,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Category string `json:"category,omitempty"`
|
||||||
|
State string `json:"state,omitempty"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
StartParameterSchema postgres.Jsonb `json:"startparameterschema,omitempty"`
|
||||||
|
CreateParameterSchema postgres.Jsonb `json:"createparameterschema,omitempty"`
|
||||||
|
ManagedExternally *bool `json:"managedexternally"`
|
||||||
|
Manager string `json:"manager,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// create IC
|
||||||
|
var newIC = ICRequest{
|
||||||
|
UUID: "7be0322d-354e-431e-84bd-ae4c9633138b",
|
||||||
|
WebsocketURL: "https://villas.k8s.eonerc.rwth-aachen.de/ws/ws_sig",
|
||||||
|
APIURL: "https://villas.k8s.eonerc.rwth-aachen.de/ws/api/v2",
|
||||||
|
Type: "kubernetes",
|
||||||
|
Name: "Kubernetes Simulator",
|
||||||
|
Category: "simulator",
|
||||||
|
State: "idle",
|
||||||
|
Location: "k8s",
|
||||||
|
Description: "A kubernetes simulator for testing purposes",
|
||||||
|
StartParameterSchema: postgres.Jsonb{json.RawMessage(`{"startprop1" : "a nice prop"}`)},
|
||||||
|
CreateParameterSchema: postgres.Jsonb{json.RawMessage(`{"createprop1" : "a really nice prop"}`)},
|
||||||
|
ManagedExternally: newTrue(),
|
||||||
|
Manager: "7be0322d-354e-431e-84bd-ae4c9633beef",
|
||||||
|
}
|
||||||
|
|
||||||
|
// fake an IC update (create) message
|
||||||
|
var update ICUpdateKubernetesJob
|
||||||
|
update.Properties.Name = newIC.Name
|
||||||
|
update.Properties.Category = newIC.Category
|
||||||
|
update.Properties.Type = newIC.Type
|
||||||
|
update.Properties.UUID = newIC.UUID
|
||||||
|
update.Properties.Job.MetaData.JobName = "myJob"
|
||||||
|
update.Properties.Job.Spec.Active = "70"
|
||||||
|
update.Status.ManagedBy = newIC.Manager
|
||||||
|
update.Status.State = newIC.State
|
||||||
|
var container Container
|
||||||
|
container.Name = "myContainer"
|
||||||
|
container.Image = "python:latest"
|
||||||
|
var Containers []Container
|
||||||
|
update.Properties.Job.Spec.Template.Spec.Containers = append(Containers, container)
|
||||||
|
|
||||||
|
payload, err := json.Marshal(update)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.CheckConnection()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.Send(payload, newIC.Manager)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addComponentConfig(scenarioID uint, token string) error {
|
||||||
|
type ConfigRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ScenarioID uint `json:"scenarioID,omitempty"`
|
||||||
|
ICID uint `json:"icID,omitempty"`
|
||||||
|
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||||
|
FileIDs []int64 `json:"fileIDs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var newConfig1 = ConfigRequest{
|
||||||
|
Name: "Example for Signal generator",
|
||||||
|
ScenarioID: scenarioID,
|
||||||
|
ICID: 1,
|
||||||
|
StartParameters: postgres.Jsonb{RawMessage: json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`)},
|
||||||
|
FileIDs: []int64{},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := helper.TestEndpoint(router, token,
|
||||||
|
"/api/v2/configs", "POST", helper.KeyModels{"config": newConfig1})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFakeIC(uuid string, session *helper.AMQPsession, token string) error {
|
||||||
|
db := database.GetDB()
|
||||||
|
var originalIC database.InfrastructureComponent
|
||||||
|
err := db.Find(&originalIC, 1).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.CheckConnection()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var update ICUpdateKubernetesJob
|
||||||
|
err = json.Unmarshal(originalIC.StatusUpdateRaw.RawMessage, &update)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
update.Properties.UUID = uuid
|
||||||
|
|
||||||
|
payload, err := json.Marshal(update)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.Send(payload, originalIC.Manager)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addSignals(token string) error {
|
||||||
|
type SignalRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Unit string `json:"unit,omitempty"`
|
||||||
|
Index uint `json:"index,omitempty"`
|
||||||
|
Direction string `json:"direction,omitempty"`
|
||||||
|
ScalingFactor float32 `json:"scalingFactor,omitempty"`
|
||||||
|
ConfigID uint `json:"configID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var newSignalOut = SignalRequest{
|
||||||
|
Name: "outSignal_A",
|
||||||
|
Unit: "V",
|
||||||
|
Direction: "out",
|
||||||
|
Index: 1,
|
||||||
|
ConfigID: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
var newSignalIn = SignalRequest{
|
||||||
|
Name: "inSignal_A",
|
||||||
|
Unit: "V",
|
||||||
|
Direction: "in",
|
||||||
|
Index: 2,
|
||||||
|
ConfigID: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := helper.TestEndpoint(router, token,
|
||||||
|
"/api/v2/signals", "POST", helper.KeyModels{"signal": newSignalOut})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = helper.TestEndpoint(router, token,
|
||||||
|
"/api/v2/signals", "POST", helper.KeyModels{"signal": newSignalIn})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (r *updateUserRequest) updatedUser(callerID interface{}, role interface{},
|
||||||
|
|
||||||
// Update the username making sure it is NOT taken
|
// Update the username making sure it is NOT taken
|
||||||
var testUser User
|
var testUser User
|
||||||
if err := testUser.ByUsername(r.User.Username); err == nil {
|
if err := testUser.byUsername(r.User.Username); err == nil {
|
||||||
return u, &UsernameAlreadyTaken{Username: r.User.Username}
|
return u, &UsernameAlreadyTaken{Username: r.User.Username}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ func (r *updateUserRequest) updatedUser(callerID interface{}, role interface{},
|
||||||
|
|
||||||
if role == "Admin" { // admin has to enter admin password
|
if role == "Admin" { // admin has to enter admin password
|
||||||
var adminUser User
|
var adminUser User
|
||||||
err := adminUser.ByID(callerID.(uint))
|
err := adminUser.byID(callerID.(uint))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return u, err
|
return u, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
func RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
||||||
|
@ -54,7 +53,7 @@ func RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getWidgets(c *gin.Context) {
|
func getWidgets(c *gin.Context) {
|
||||||
|
|
||||||
ok, dab := dashboard.CheckPermissions(c, database.Read, "query", -1)
|
ok, dab := database.CheckDashboardPermissions(c, database.Read, "query", -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -100,7 +99,7 @@ func addWidget(c *gin.Context) {
|
||||||
newWidget := req.createWidget()
|
newWidget := req.createWidget()
|
||||||
|
|
||||||
// Check if user is allowed to modify selected dashboard (scenario)
|
// Check if user is allowed to modify selected dashboard (scenario)
|
||||||
ok, _ := dashboard.CheckPermissions(c, database.Update, "body", int(newWidget.DashboardID))
|
ok, _ := database.CheckDashboardPermissions(c, database.Update, "body", int(newWidget.DashboardID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -129,11 +128,14 @@ func addWidget(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func updateWidget(c *gin.Context) {
|
func updateWidget(c *gin.Context) {
|
||||||
|
|
||||||
ok, oldWidget := CheckPermissions(c, database.Update, -1)
|
ok, oldWidget_r := database.CheckWidgetPermissions(c, database.Update, -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldWidget Widget
|
||||||
|
oldWidget.Widget = oldWidget_r
|
||||||
|
|
||||||
var req updateWidgetRequest
|
var req updateWidgetRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.BadRequestError(c, err.Error())
|
helper.BadRequestError(c, err.Error())
|
||||||
|
@ -172,12 +174,12 @@ func updateWidget(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func getWidget(c *gin.Context) {
|
func getWidget(c *gin.Context) {
|
||||||
|
|
||||||
ok, w := CheckPermissions(c, database.Read, -1)
|
ok, w := database.CheckWidgetPermissions(c, database.Read, -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"widget": w.Widget})
|
c.JSON(http.StatusOK, gin.H{"widget": w})
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteWidget godoc
|
// deleteWidget godoc
|
||||||
|
@ -195,11 +197,14 @@ func getWidget(c *gin.Context) {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func deleteWidget(c *gin.Context) {
|
func deleteWidget(c *gin.Context) {
|
||||||
|
|
||||||
ok, w := CheckPermissions(c, database.Delete, -1)
|
ok, w_r := database.CheckWidgetPermissions(c, database.Delete, -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var w Widget
|
||||||
|
w.Widget = w_r
|
||||||
|
|
||||||
err := w.delete()
|
err := w.delete()
|
||||||
if !helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
c.JSON(http.StatusOK, gin.H{"widget": w.Widget})
|
c.JSON(http.StatusOK, gin.H{"widget": w.Widget})
|
||||||
|
|
|
@ -23,7 +23,6 @@ package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Widget struct {
|
type Widget struct {
|
||||||
|
@ -47,8 +46,8 @@ func (w *Widget) ByID(id uint) error {
|
||||||
|
|
||||||
func (w *Widget) addToDashboard() error {
|
func (w *Widget) addToDashboard() error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var dab dashboard.Dashboard
|
var dab database.Dashboard
|
||||||
err := dab.ByID(uint(w.DashboardID))
|
err := db.Find(&dab, uint(w.DashboardID)).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -59,7 +58,7 @@ func (w *Widget) addToDashboard() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// associate dashboard with simulation
|
// associate widget with dashboard
|
||||||
err = db.Model(&dab).Association("Widgets").Append(w).Error
|
err = db.Model(&dab).Association("Widgets").Append(w).Error
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -89,8 +88,8 @@ func (w *Widget) update(modifiedWidget Widget) error {
|
||||||
func (w *Widget) delete() error {
|
func (w *Widget) delete() error {
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var dab dashboard.Dashboard
|
var dab database.Dashboard
|
||||||
err := dab.ByID(w.DashboardID)
|
err := db.Find(&dab, uint(w.DashboardID)).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
/** Widget package, middleware.
|
|
||||||
*
|
|
||||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
||||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
||||||
* @license GNU General Public License (version 3)
|
|
||||||
*
|
|
||||||
* VILLASweb-backend-go
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*********************************************************************************/
|
|
||||||
package widget
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckPermissions(c *gin.Context, operation database.CRUD, widgetIDBody int) (bool, Widget) {
|
|
||||||
|
|
||||||
var w Widget
|
|
||||||
var err error
|
|
||||||
err = database.ValidateRole(c, database.ModelWidget, operation)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of widget failed): %v", err.Error()))
|
|
||||||
return false, w
|
|
||||||
}
|
|
||||||
|
|
||||||
var widgetID int
|
|
||||||
if widgetIDBody < 0 {
|
|
||||||
widgetID, err = helper.GetIDOfElement(c, "widgetID", "path", -1)
|
|
||||||
if err != nil {
|
|
||||||
return false, w
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
widgetID = widgetIDBody
|
|
||||||
}
|
|
||||||
|
|
||||||
err = w.ByID(uint(widgetID))
|
|
||||||
if helper.DBError(c, err) {
|
|
||||||
return false, w
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, _ := dashboard.CheckPermissions(c, operation, "body", int(w.DashboardID))
|
|
||||||
if !ok {
|
|
||||||
return false, w
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, w
|
|
||||||
}
|
|
|
@ -147,17 +147,17 @@ func TestMain(m *testing.M) {
|
||||||
func TestAddWidget(t *testing.T) {
|
func TestAddWidget(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
_, dashboardID := addScenarioAndDashboard(token)
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
|
|
||||||
newWidget.DashboardID = dashboardID
|
newWidget.DashboardID = dashboardID
|
||||||
// authenticate as userB who has no access to scenario
|
// authenticate as userB who has no access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST the newWidget with no access to the scenario
|
// try to POST the newWidget with no access to the scenario
|
||||||
|
@ -168,7 +168,7 @@ func TestAddWidget(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to POST non JSON body
|
// try to POST non JSON body
|
||||||
|
@ -214,7 +214,7 @@ func TestAddWidget(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as userB who has no access to scenario
|
// authenticate as userB who has no access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to GET the newWidget with no access to the scenario
|
// try to GET the newWidget with no access to the scenario
|
||||||
|
@ -228,10 +228,10 @@ func TestAddWidget(t *testing.T) {
|
||||||
func TestUpdateWidget(t *testing.T) {
|
func TestUpdateWidget(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
_, dashboardID := addScenarioAndDashboard(token)
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
|
@ -259,7 +259,7 @@ func TestUpdateWidget(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate as userB who has no access to scenario
|
// authenticate as userB who has no access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT the updatedWidget with no access to the scenario
|
// try to PUT the updatedWidget with no access to the scenario
|
||||||
|
@ -270,7 +270,7 @@ func TestUpdateWidget(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as guest user who has access to scenario
|
// authenticate as guest user who has access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT as guest
|
// try to PUT as guest
|
||||||
|
@ -281,7 +281,7 @@ func TestUpdateWidget(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to PUT non JSON body
|
// try to PUT non JSON body
|
||||||
|
@ -322,10 +322,10 @@ func TestUpdateWidget(t *testing.T) {
|
||||||
func TestDeleteWidget(t *testing.T) {
|
func TestDeleteWidget(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
_, dashboardID := addScenarioAndDashboard(token)
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
|
@ -342,7 +342,7 @@ func TestDeleteWidget(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// authenticate as userB who has no access to scenario
|
// authenticate as userB who has no access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to DELETE the newWidget with no access to the scenario
|
// try to DELETE the newWidget with no access to the scenario
|
||||||
|
@ -353,7 +353,7 @@ func TestDeleteWidget(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the widgets returned for dashboard
|
// Count the number of all the widgets returned for dashboard
|
||||||
|
@ -382,16 +382,16 @@ func TestDeleteWidget(t *testing.T) {
|
||||||
func TestGetAllWidgetsOfDashboard(t *testing.T) {
|
func TestGetAllWidgetsOfDashboard(t *testing.T) {
|
||||||
database.DropTables()
|
database.DropTables()
|
||||||
database.MigrateModels()
|
database.MigrateModels()
|
||||||
assert.NoError(t, helper.AddTestUsers())
|
assert.NoError(t, database.AddTestUsers())
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
_, dashboardID := addScenarioAndDashboard(token)
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
|
|
||||||
// authenticate as userB who has no access to scenario
|
// authenticate as userB who has no access to scenario
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// try to GET all widgets of dashboard
|
// try to GET all widgets of dashboard
|
||||||
|
@ -402,7 +402,7 @@ func TestGetAllWidgetsOfDashboard(t *testing.T) {
|
||||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// authenticate as normal user
|
// authenticate as normal user
|
||||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Count the number of all the widgets returned for dashboard
|
// Count the number of all the widgets returned for dashboard
|
||||||
|
|
23
start.go
23
start.go
|
@ -23,6 +23,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ import (
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/healthz"
|
||||||
infrastructure_component "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
infrastructure_component "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/zpatrick/go-config"
|
"github.com/zpatrick/go-config"
|
||||||
|
@ -121,16 +123,21 @@ func main() {
|
||||||
|
|
||||||
if amqpHost != "" {
|
if amqpHost != "" {
|
||||||
// create amqp URL based on username, password and host
|
// create amqp URL based on username, password and host
|
||||||
amqpURL := "amqp://" + amqpUser + ":" + amqpPass + "@" + amqpHost
|
amqpurl := "amqp://" + amqpUser + ":" + amqpPass + "@" + amqpHost
|
||||||
err = infrastructure_component.StartAMQP(amqpURL, api)
|
session := helper.NewAMQPSession("villas-amqp-session", amqpurl, "villas", infrastructure_component.ProcessMessage)
|
||||||
if err != nil {
|
healthz.SetAMQPSession(session) // healthz needs to know the amqp session to check the health of the backend
|
||||||
log.Fatal(err)
|
infrastructure_component.SetAMQPSession(session) // IC needs to know the session to send amqp messages
|
||||||
}
|
user.SetAMQPSession(session) // User needs to know the session to duplicate ICs upon login
|
||||||
|
|
||||||
// send Ping to all externally managed ICs
|
// send Ping to all externally managed ICs
|
||||||
err = helper.SendPing("")
|
for {
|
||||||
if err != nil {
|
if session.IsReady {
|
||||||
log.Println("error sending ping action via AMQP: ", err)
|
err = infrastructure_component.SendPing("")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error sending ping action via AMQP: ", err.Error())
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue