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()
|
||||
}
|
||||
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
||||
"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/httptest"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nsf/jsondiff"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// data type used in testing
|
||||
|
@ -42,28 +40,6 @@ type KeyModels map[string]interface{}
|
|||
// #################### 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 {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
|
@ -73,31 +49,6 @@ type UserRequest struct {
|
|||
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 ############################
|
||||
// ############################################################################
|
||||
|
@ -315,20 +266,3 @@ func AuthenticateForTest(router *gin.Engine, credentials interface{}) (string, e
|
|||
// Return the token and nil error
|
||||
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"
|
||||
|
||||
"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) {
|
||||
|
@ -54,7 +53,7 @@ func RegisterComponentConfigurationEndpoints(r *gin.RouterGroup) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -102,7 +101,7 @@ func addConfig(c *gin.Context) {
|
|||
newConfig := req.createConfig()
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
@ -132,11 +131,15 @@ func addConfig(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var oldConfig ComponentConfiguration
|
||||
oldConfig.ComponentConfiguration = oldConfig_r
|
||||
|
||||
var req updateConfigRequest
|
||||
err := c.BindJSON(&req)
|
||||
if err != nil {
|
||||
|
@ -176,12 +179,12 @@ func updateConfig(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func getConfig(c *gin.Context) {
|
||||
|
||||
ok, m := CheckPermissions(c, database.Read, "path", -1)
|
||||
ok, m := database.CheckComponentConfigPermissions(c, database.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"config": m.ComponentConfiguration})
|
||||
c.JSON(http.StatusOK, gin.H{"config": m})
|
||||
}
|
||||
|
||||
// deleteConfig godoc
|
||||
|
@ -199,11 +202,14 @@ func getConfig(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var m ComponentConfiguration
|
||||
m.ComponentConfiguration = m_r
|
||||
|
||||
err := m.delete()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"config": m.ComponentConfiguration})
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"log"
|
||||
|
||||
"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 {
|
||||
|
@ -49,8 +48,8 @@ func (m *ComponentConfiguration) ByID(id uint) error {
|
|||
|
||||
func (m *ComponentConfiguration) addToScenario() error {
|
||||
db := database.GetDB()
|
||||
var so scenario.Scenario
|
||||
err := so.ByID(m.ScenarioID)
|
||||
var so database.Scenario
|
||||
err := db.Find(&so, m.ScenarioID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -120,8 +119,8 @@ func (m *ComponentConfiguration) Update(modifiedConfig ComponentConfiguration) e
|
|||
func (m *ComponentConfiguration) delete() error {
|
||||
|
||||
db := database.GetDB()
|
||||
var so scenario.Scenario
|
||||
err := so.ByID(m.ScenarioID)
|
||||
var so database.Scenario
|
||||
err := db.Find(&so, m.ScenarioID).Error
|
||||
if err != nil {
|
||||
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) {
|
||||
|
||||
// authenticate as admin
|
||||
token, _ := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, _ := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
|
||||
// POST $newICA
|
||||
newICA := ICRequest{
|
||||
|
@ -116,7 +116,7 @@ func addScenarioAndIC() (scenarioID uint, ICID uint) {
|
|||
}
|
||||
|
||||
// authenticate as normal user
|
||||
token, _ = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, _ = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
|
||||
// POST $newScenario
|
||||
newScenario := ScenarioRequest{
|
||||
|
@ -172,7 +172,7 @@ func TestMain(m *testing.M) {
|
|||
func TestAddConfig(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -182,7 +182,7 @@ func TestAddConfig(t *testing.T) {
|
|||
newConfig1.ScenarioID = scenarioID
|
||||
newConfig1.ICID = ICID
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST newConfig
|
||||
|
@ -242,7 +242,7 @@ func TestAddConfig(t *testing.T) {
|
|||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// 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)
|
||||
|
||||
// Try to GET the newConfig with no access
|
||||
|
@ -258,7 +258,7 @@ func TestUpdateConfig(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -266,7 +266,7 @@ func TestUpdateConfig(t *testing.T) {
|
|||
scenarioID, ICID := addScenarioAndIC()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST newConfig
|
||||
|
@ -287,7 +287,7 @@ func TestUpdateConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to PUT a non JSON body
|
||||
|
@ -361,7 +361,7 @@ func TestUpdateConfig(t *testing.T) {
|
|||
func TestDeleteConfig(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -371,7 +371,7 @@ func TestDeleteConfig(t *testing.T) {
|
|||
newConfig1.ICID = ICID
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST newConfig
|
||||
|
@ -385,7 +385,7 @@ func TestDeleteConfig(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -435,7 +435,7 @@ func TestGetAllConfigsOfScenario(t *testing.T) {
|
|||
newConfig1.ICID = ICID
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST newConfig
|
||||
|
@ -452,7 +452,7 @@ func TestGetAllConfigsOfScenario(t *testing.T) {
|
|||
assert.Equal(t, 1, NumberOfConfigs)
|
||||
|
||||
// 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)
|
||||
|
||||
// try to get configs without access
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
"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 RegisterDashboardEndpoints(r *gin.RouterGroup) {
|
||||
|
@ -55,7 +54,7 @@ func RegisterDashboardEndpoints(r *gin.RouterGroup) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -102,7 +101,7 @@ func addDashboard(c *gin.Context) {
|
|||
newDashboard := req.createDashboard()
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
@ -132,11 +131,14 @@ func addDashboard(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var oldDashboard Dashboard
|
||||
oldDashboard.Dashboard = oldDashboard_r
|
||||
|
||||
var req updateDashboardRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.BadRequestError(c, err.Error())
|
||||
|
@ -174,12 +176,12 @@ func updateDashboard(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func getDashboard(c *gin.Context) {
|
||||
|
||||
ok, dab := CheckPermissions(c, database.Read, "path", -1)
|
||||
ok, dab := database.CheckDashboardPermissions(c, database.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"dashboard": dab.Dashboard})
|
||||
c.JSON(http.StatusOK, gin.H{"dashboard": dab})
|
||||
}
|
||||
|
||||
// deleteDashboard godoc
|
||||
|
@ -196,11 +198,14 @@ func getDashboard(c *gin.Context) {
|
|||
// @Router /dashboards/{dashboardID} [delete]
|
||||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var dab Dashboard
|
||||
dab.Dashboard = dab_r
|
||||
|
||||
err := dab.delete()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"dashboard": dab.Dashboard})
|
||||
|
|
|
@ -23,7 +23,6 @@ package dashboard
|
|||
|
||||
import (
|
||||
"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 {
|
||||
|
@ -47,8 +46,8 @@ func (d *Dashboard) ByID(id uint) error {
|
|||
|
||||
func (d *Dashboard) addToScenario() error {
|
||||
db := database.GetDB()
|
||||
var sim scenario.Scenario
|
||||
err := sim.ByID(d.ScenarioID)
|
||||
var sim database.Scenario
|
||||
err := db.Find(&sim, d.ScenarioID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -81,8 +80,8 @@ func (d *Dashboard) update(modifiedDab Dashboard) error {
|
|||
func (d *Dashboard) delete() error {
|
||||
|
||||
db := database.GetDB()
|
||||
var sim scenario.Scenario
|
||||
err := sim.ByID(d.ScenarioID)
|
||||
var sim database.Scenario
|
||||
err := db.Find(&sim, d.ScenarioID).Error
|
||||
if err != nil {
|
||||
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) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
scenarioID := addScenario(token)
|
||||
|
@ -168,7 +168,7 @@ func TestAddDashboard(t *testing.T) {
|
|||
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)
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// this should fail with unprocessable entity
|
||||
|
@ -189,10 +189,10 @@ func TestAddDashboard(t *testing.T) {
|
|||
func TestUpdateDashboard(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
scenarioID := addScenario(token)
|
||||
|
@ -214,7 +214,7 @@ func TestUpdateDashboard(t *testing.T) {
|
|||
}
|
||||
|
||||
// authenticate as guest user
|
||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
|
@ -263,10 +263,10 @@ func TestUpdateDashboard(t *testing.T) {
|
|||
func TestDeleteDashboard(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
scenarioID := addScenario(token)
|
||||
|
@ -283,7 +283,7 @@ func TestDeleteDashboard(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
scenarioID := addScenario(token)
|
||||
|
@ -374,7 +374,7 @@ func TestGetAllDashboardsOfScenario(t *testing.T) {
|
|||
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
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// this should fail with unprocessable entity
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
"net/http"
|
||||
|
||||
"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"
|
||||
|
@ -54,7 +53,7 @@ func RegisterFileEndpoints(r *gin.RouterGroup) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -92,7 +91,7 @@ func getFiles(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -142,11 +141,14 @@ func addFile(c *gin.Context) {
|
|||
func getFile(c *gin.Context) {
|
||||
|
||||
// check access
|
||||
ok, f := CheckPermissions(c, database.Read)
|
||||
ok, f_r := database.CheckFilePermissions(c, database.Read)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var f File
|
||||
f.File = f_r
|
||||
|
||||
err := f.download(c)
|
||||
helper.DBError(c, err)
|
||||
}
|
||||
|
@ -175,11 +177,14 @@ func getFile(c *gin.Context) {
|
|||
func updateFile(c *gin.Context) {
|
||||
|
||||
// check access
|
||||
ok, f := CheckPermissions(c, database.Update)
|
||||
ok, f_r := database.CheckFilePermissions(c, database.Update)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var f File
|
||||
f.File = f_r
|
||||
|
||||
// Extract file from PUT request form
|
||||
fileHeader, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
|
@ -209,11 +214,14 @@ func updateFile(c *gin.Context) {
|
|||
func deleteFile(c *gin.Context) {
|
||||
|
||||
// check access
|
||||
ok, f := CheckPermissions(c, database.Delete)
|
||||
ok, f_r := database.CheckFilePermissions(c, database.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var f File
|
||||
f.File = f_r
|
||||
|
||||
err := f.Delete()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"file": f.File})
|
||||
|
|
|
@ -36,7 +36,6 @@ import (
|
|||
"time"
|
||||
|
||||
"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"
|
||||
|
||||
"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
|
||||
db := database.GetDB()
|
||||
|
||||
var so scenario.Scenario
|
||||
err = so.ByID(scenarioID)
|
||||
var so database.Scenario
|
||||
err = db.Find(&so, scenarioID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -229,8 +228,8 @@ func (f *File) Delete() error {
|
|||
db := database.GetDB()
|
||||
|
||||
// remove association between file and scenario
|
||||
var so scenario.Scenario
|
||||
err := so.ByID(f.ScenarioID)
|
||||
var so database.Scenario
|
||||
err := db.Find(&so, f.ScenarioID).Error
|
||||
if err != nil {
|
||||
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) {
|
||||
|
||||
// authenticate as admin
|
||||
_, _ = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
_, _ = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
|
||||
// authenticate as normal user
|
||||
token, _ := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, _ := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
|
||||
// POST $newScenario
|
||||
newScenario := ScenarioRequest{
|
||||
|
@ -103,14 +103,14 @@ func TestMain(m *testing.M) {
|
|||
func TestAddFile(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
scenarioID := addScenario()
|
||||
|
||||
// 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)
|
||||
|
||||
emptyBuf := &bytes.Buffer{}
|
||||
|
@ -123,7 +123,7 @@ func TestAddFile(t *testing.T) {
|
|||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// authenticate as normal userA
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to POST without a scenario ID
|
||||
|
@ -189,14 +189,14 @@ func TestUpdateFile(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
scenarioID := addScenario()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// create a testfile.txt in local folder
|
||||
|
@ -236,7 +236,7 @@ func TestUpdateFile(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
emptyBuf := &bytes.Buffer{}
|
||||
|
@ -249,7 +249,7 @@ func TestUpdateFile(t *testing.T) {
|
|||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// authenticate as guest user C
|
||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to PUT as guest
|
||||
|
@ -261,7 +261,7 @@ func TestUpdateFile(t *testing.T) {
|
|||
|
||||
// Prepare update
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to PUT with empty body
|
||||
|
@ -319,14 +319,14 @@ func TestUpdateFile(t *testing.T) {
|
|||
func TestDeleteFile(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
scenarioID := addScenario()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// create a testfile.txt in local folder
|
||||
|
@ -386,7 +386,7 @@ func TestDeleteFile(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as guest user C
|
||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete the added file 1
|
||||
|
@ -451,14 +451,14 @@ func TestGetAllFilesOfScenario(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
scenarioID := addScenario()
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal userA
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
//try to get all files with missing scenario ID; should return a bad request error
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
package healthz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
|
@ -31,11 +32,17 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var session *helper.AMQPsession
|
||||
|
||||
func RegisterHealthzEndpoint(r *gin.RouterGroup) {
|
||||
|
||||
r.GET("", getHealth)
|
||||
}
|
||||
|
||||
func SetAMQPSession(s *helper.AMQPsession) {
|
||||
session = s
|
||||
}
|
||||
|
||||
// getHealth godoc
|
||||
// @Summary Get health status of backend
|
||||
// @ID getHealth
|
||||
|
@ -67,12 +74,20 @@ func getHealth(c *gin.Context) {
|
|||
c.Writer.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
} else {
|
||||
err = helper.CheckConnection()
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
if session != nil {
|
||||
err = session.CheckConnection()
|
||||
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{
|
||||
"success:": false,
|
||||
"message": err.Error(),
|
||||
"message": fmt.Errorf("AMQP session is nil"),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -80,8 +80,14 @@ func TestHealthz(t *testing.T) {
|
|||
amqpURI := "amqp://" + user + ":" + pass + "@" + host
|
||||
log.Println("AMQP URI is", amqpURI)
|
||||
|
||||
err = helper.ConnectAMQP(amqpURI, infrastructure_component.ProcessMessage)
|
||||
assert.NoError(t, err)
|
||||
session = helper.NewAMQPSession("villas-test-session", amqpURI, "villas", infrastructure_component.ProcessMessage)
|
||||
SetAMQPSession(session)
|
||||
|
||||
for {
|
||||
if session.IsReady {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// test healthz endpoint for connected DB and AMQP client
|
||||
code, resp, err = helper.TestEndpoint(router, "", "/api/v2/healthz", http.MethodGet, nil)
|
||||
|
|
|
@ -26,14 +26,20 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/jinzhu/gorm/dialects/postgres"
|
||||
"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 {
|
||||
State string `json:"state"`
|
||||
Version string `json:"version"`
|
||||
|
@ -68,42 +74,24 @@ type ICUpdate struct {
|
|||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
func StartAMQP(AMQPurl string, api *gin.RouterGroup) error {
|
||||
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 {
|
||||
func ProcessMessage(message amqp.Delivery) {
|
||||
|
||||
var payload ICUpdate
|
||||
err := json.Unmarshal(message.Body, &payload)
|
||||
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 a message contains an action, it is not intended for the backend
|
||||
//log.Println("AMQP: Ignoring action message ", payload)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
ICUUID := payload.Properties.UUID
|
||||
_, err = uuid.Parse(ICUUID)
|
||||
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
|
||||
|
@ -119,8 +107,10 @@ func ProcessMessage(message amqp.Delivery) error {
|
|||
// update record based on payload
|
||||
err = sToBeUpdated.updateExternalIC(payload, message.Body)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// send ping to get full status update of this IC
|
||||
err = helper.SendPing(ICUUID)
|
||||
err = SendPing(ICUUID)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -253,3 +243,45 @@ func newFalse() *bool {
|
|||
b := false
|
||||
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
|
||||
var x InfrastructureComponent
|
||||
err = x.byID(ic.ID)
|
||||
err = x.ByID(ic.ID)
|
||||
if err != nil {
|
||||
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
|
||||
var x InfrastructureComponent
|
||||
err = x.byID(ic.ID)
|
||||
err = x.ByID(ic.ID)
|
||||
if err != nil {
|
||||
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.DELETE("/:ICID", deleteIC)
|
||||
r.GET("/:ICID/configs", getConfigsOfIC)
|
||||
r.POST("/:ICID/action", sendActionToIC)
|
||||
}
|
||||
|
||||
func RegisterAMQPEndpoint(r *gin.RouterGroup) {
|
||||
r.POST("/:ICID/action", sendActionToIC)
|
||||
var session *helper.AMQPsession
|
||||
|
||||
func SetAMQPSession(s *helper.AMQPsession) {
|
||||
session = s
|
||||
}
|
||||
|
||||
// getICs godoc
|
||||
|
@ -83,7 +86,7 @@ func getICs(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func addIC(c *gin.Context) {
|
||||
|
||||
ok, _ := CheckPermissions(c, database.ModelInfrastructureComponent, database.Create, false)
|
||||
ok, _ := database.CheckICPermissions(c, database.ModelInfrastructureComponent, database.Create, false)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
@ -144,11 +147,14 @@ func addIC(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var oldIC InfrastructureComponent
|
||||
oldIC.InfrastructureComponent = oldIC_r
|
||||
|
||||
if oldIC.ManagedExternally {
|
||||
helper.BadRequestError(c, "Cannot update externally managed component via API")
|
||||
return
|
||||
|
@ -193,12 +199,12 @@ func updateIC(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ic": s.InfrastructureComponent})
|
||||
c.JSON(http.StatusOK, gin.H{"ic": s})
|
||||
}
|
||||
|
||||
// deleteIC godoc
|
||||
|
@ -216,11 +222,14 @@ func getIC(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var s InfrastructureComponent
|
||||
s.InfrastructureComponent = s_r
|
||||
|
||||
// Check if IC is managed externally
|
||||
if s.ManagedExternally {
|
||||
// if so: refuse deletion
|
||||
|
@ -255,11 +264,14 @@ func deleteIC(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var s InfrastructureComponent
|
||||
s.InfrastructureComponent = s_r
|
||||
|
||||
// get all associated configurations
|
||||
allConfigs, _, err := s.getConfigs()
|
||||
if !helper.DBError(c, err) {
|
||||
|
@ -284,12 +296,12 @@ func getConfigsOfIC(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var actions []helper.Action
|
||||
var actions []Action
|
||||
err := c.BindJSON(&actions)
|
||||
if err != nil {
|
||||
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" {
|
||||
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 {
|
||||
helper.InternalServerError(c, "Unable to send actions to IC: "+err.Error())
|
||||
return
|
||||
|
|
|
@ -35,7 +35,7 @@ func (s *InfrastructureComponent) save() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (s *InfrastructureComponent) byID(id uint) error {
|
||||
func (s *InfrastructureComponent) ByID(id uint) error {
|
||||
db := database.GetDB()
|
||||
err := db.Find(s, id).Error
|
||||
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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/streadway/amqp"
|
||||
|
||||
"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"
|
||||
"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
|
||||
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||
|
||||
// check AMQP connection
|
||||
err = helper.CheckConnection()
|
||||
if err.Error() != "connection is nil" {
|
||||
return
|
||||
}
|
||||
|
||||
// connect AMQP client
|
||||
// Make sure that AMQP_HOST, AMQP_USER, AMQP_PASS are set
|
||||
host, _ := configuration.GlobalConfig.String("amqp.host")
|
||||
|
@ -148,11 +139,8 @@ func TestMain(m *testing.M) {
|
|||
|
||||
// AMQP Connection startup is tested here
|
||||
// Not repeated in other tests because it is only needed once
|
||||
err = StartAMQP(amqpURI, api)
|
||||
if err != nil {
|
||||
log.Println("unable to connect to AMQP")
|
||||
return
|
||||
}
|
||||
session = helper.NewAMQPSession("villas-test-session", amqpURI, "villas", ProcessMessage)
|
||||
SetAMQPSession(session)
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
@ -160,10 +148,10 @@ func TestMain(m *testing.M) {
|
|||
func TestAddICAsAdmin(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to POST with non JSON body
|
||||
|
@ -225,10 +213,10 @@ func TestAddICAsAdmin(t *testing.T) {
|
|||
func TestAddICAsUser(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST ic/ $newIC
|
||||
|
@ -244,10 +232,10 @@ func TestAddICAsUser(t *testing.T) {
|
|||
func TestUpdateICAsAdmin(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST ic/ $newIC
|
||||
|
@ -306,23 +294,14 @@ func TestUpdateICAsAdmin(t *testing.T) {
|
|||
payload, err := json.Marshal(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
||||
headers["uuid"] = newIC2.Manager // set uuid
|
||||
//var headers map[string]interface{}
|
||||
//headers = make(map[string]interface{}) // empty map
|
||||
//headers["uuid"] = newIC2.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()
|
||||
err = session.CheckConnection()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = helper.PublishAMQP(msg)
|
||||
err = session.Send(payload, newIC2.Manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Wait until externally managed IC is created (happens async)
|
||||
|
@ -342,10 +321,10 @@ func TestUpdateICAsAdmin(t *testing.T) {
|
|||
func TestUpdateICAsUser(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST ic/ $newIC
|
||||
|
@ -359,7 +338,7 @@ func TestUpdateICAsUser(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// authenticate as user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test PUT IC
|
||||
|
@ -375,10 +354,10 @@ func TestUpdateICAsUser(t *testing.T) {
|
|||
func TestDeleteICAsAdmin(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST ic/ $newIC
|
||||
|
@ -425,23 +404,10 @@ func TestDeleteICAsAdmin(t *testing.T) {
|
|||
payload, err := json.Marshal(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
||||
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()
|
||||
err = session.CheckConnection()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = helper.PublishAMQP(msg)
|
||||
err = session.Send(payload, newIC2.UUID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Wait until externally managed IC is created (happens async)
|
||||
|
@ -465,10 +431,10 @@ func TestDeleteICAsAdmin(t *testing.T) {
|
|||
func TestDeleteICAsUser(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST ic/ $newIC
|
||||
|
@ -482,7 +448,7 @@ func TestDeleteICAsUser(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// authenticate as user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test DELETE ICs
|
||||
|
@ -497,10 +463,10 @@ func TestDeleteICAsUser(t *testing.T) {
|
|||
func TestGetAllICs(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// get the length of the GET all ICs response again
|
||||
|
@ -543,10 +509,10 @@ func TestGetAllICs(t *testing.T) {
|
|||
func TestGetConfigsOfIC(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST ic/ $newICA
|
||||
|
@ -568,7 +534,7 @@ func TestGetConfigsOfIC(t *testing.T) {
|
|||
assert.Equal(t, 0, numberOfConfigs)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test GET ic/ID/configs
|
||||
|
@ -590,10 +556,10 @@ func TestGetConfigsOfIC(t *testing.T) {
|
|||
func TestSendActionToIC(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST ic/ $newICA
|
||||
|
@ -607,7 +573,7 @@ func TestSendActionToIC(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// create action to be sent to IC
|
||||
action1 := helper.Action{
|
||||
action1 := Action{
|
||||
Act: "start",
|
||||
When: time.Now().Unix(),
|
||||
}
|
||||
|
@ -620,7 +586,7 @@ func TestSendActionToIC(t *testing.T) {
|
|||
|
||||
paramsRaw, _ := json.Marshal(¶ms)
|
||||
action1.Parameters = paramsRaw
|
||||
actions := [1]helper.Action{action1}
|
||||
actions := [1]Action{action1}
|
||||
|
||||
// Send action to IC
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
|
@ -639,10 +605,10 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// fake an IC update message
|
||||
|
@ -652,23 +618,10 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
|||
payload, err := json.Marshal(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
||||
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()
|
||||
err = session.CheckConnection()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = helper.PublishAMQP(msg)
|
||||
err = session.Send(payload, newIC1.Manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(waitingTime * time.Second)
|
||||
|
@ -694,20 +647,7 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
|||
payload, err = json.Marshal(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var headersA map[string]interface{} = make(map[string]interface{}) // empty map
|
||||
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)
|
||||
err = session.Send(payload, newIC1.Manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(waitingTime * time.Second)
|
||||
|
@ -723,17 +663,7 @@ func TestCreateUpdateViaAMQPRecv(t *testing.T) {
|
|||
payload, err = json.Marshal(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
msg = amqp.Publishing{
|
||||
DeliveryMode: 2,
|
||||
Timestamp: time.Now(),
|
||||
ContentType: "application/json",
|
||||
ContentEncoding: "utf-8",
|
||||
Priority: 0,
|
||||
Body: payload,
|
||||
Headers: headersA,
|
||||
}
|
||||
|
||||
err = helper.PublishAMQP(msg)
|
||||
err = session.Send(payload, newIC1.Manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(waitingTime * time.Second)
|
||||
|
@ -749,10 +679,10 @@ func TestDeleteICViaAMQPRecv(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// fake an IC update message
|
||||
|
@ -773,22 +703,10 @@ func TestDeleteICViaAMQPRecv(t *testing.T) {
|
|||
payload, err := json.Marshal(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var headers map[string]interface{} = make(map[string]interface{}) // empty map
|
||||
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()
|
||||
err = session.CheckConnection()
|
||||
assert.NoError(t, err)
|
||||
err = helper.PublishAMQP(msg)
|
||||
|
||||
err = session.Send(payload, newIC1.Manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(waitingTime * time.Second)
|
||||
|
@ -847,18 +765,7 @@ func TestDeleteICViaAMQPRecv(t *testing.T) {
|
|||
payload, err = json.Marshal(update)
|
||||
assert.NoError(t, err)
|
||||
|
||||
msg = amqp.Publishing{
|
||||
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)
|
||||
err = session.Send(payload, newIC1.Manager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(waitingTime * time.Second)
|
||||
|
|
|
@ -151,7 +151,7 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
var Admin = helper.Credentials{
|
||||
var Admin = database.Credentials{
|
||||
Username: adminName,
|
||||
Password: adminPW,
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ import (
|
|||
"os"
|
||||
"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/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -55,30 +53,6 @@ func TestMain(m *testing.M) {
|
|||
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) {
|
||||
database.DropTables()
|
||||
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/helper"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -57,7 +56,7 @@ func RegisterResultEndpoints(r *gin.RouterGroup) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -103,7 +102,7 @@ func addResult(c *gin.Context) {
|
|||
newResult := req.createResult()
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
@ -133,11 +132,14 @@ func addResult(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var oldResult Result
|
||||
oldResult.Result = oldResult_r
|
||||
|
||||
var req updateResultRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.BadRequestError(c, err.Error())
|
||||
|
@ -175,12 +177,12 @@ func updateResult(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func getResult(c *gin.Context) {
|
||||
|
||||
ok, result := checkPermissions(c, database.Read, "path", -1)
|
||||
ok, result := database.CheckResultPermissions(c, database.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"result": result.Result})
|
||||
c.JSON(http.StatusOK, gin.H{"result": result})
|
||||
}
|
||||
|
||||
// deleteResult godoc
|
||||
|
@ -197,13 +199,16 @@ func getResult(c *gin.Context) {
|
|||
// @Router /results/{resultID} [delete]
|
||||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var result Result
|
||||
result.Result = result_r
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
@ -240,13 +245,16 @@ func deleteResult(c *gin.Context) {
|
|||
// @Router /results/{resultID}/file [post]
|
||||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var result Result
|
||||
result.Result = result_r
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
@ -290,18 +298,24 @@ func addResultFile(c *gin.Context) {
|
|||
func deleteResultFile(c *gin.Context) {
|
||||
|
||||
// check access
|
||||
ok, result := checkPermissions(c, database.Update, "path", -1)
|
||||
ok, result_r := database.CheckResultPermissions(c, database.Update, "path", -1)
|
||||
if !ok {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var f file.File
|
||||
f.File = f_r
|
||||
|
||||
// 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 {
|
||||
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/routes/file"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
|
@ -51,8 +50,8 @@ func (r *Result) ByID(id uint) error {
|
|||
|
||||
func (r *Result) addToScenario() error {
|
||||
db := database.GetDB()
|
||||
var sco scenario.Scenario
|
||||
err := sco.ByID(r.ScenarioID)
|
||||
var sco database.Scenario
|
||||
err := db.Find(&sco, r.ScenarioID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -85,8 +84,8 @@ func (r *Result) update(modifiedResult Result) error {
|
|||
func (r *Result) delete() error {
|
||||
|
||||
db := database.GetDB()
|
||||
var sco scenario.Scenario
|
||||
err := sco.ByID(r.ScenarioID)
|
||||
var sco database.Scenario
|
||||
err := db.Find(&sco, r.ScenarioID).Error
|
||||
if err != nil {
|
||||
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) {
|
||||
|
||||
// authenticate as admin
|
||||
helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
_, _ = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
|
||||
// authenticate as normal user
|
||||
token, _ := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, _ := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
|
||||
// POST $newScenario
|
||||
newScenario := ScenarioRequest{
|
||||
|
@ -123,14 +123,14 @@ func TestGetAllResultsOfScenario(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario
|
||||
scenarioID := addScenario()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST newResult
|
||||
|
@ -154,7 +154,7 @@ func TestGetAllResultsOfScenario(t *testing.T) {
|
|||
assert.Equal(t, 1, NumberOfConfigs)
|
||||
|
||||
// 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)
|
||||
|
||||
// try to get results without access
|
||||
|
@ -170,7 +170,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario
|
||||
|
@ -182,7 +182,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
|||
newResult.ScenarioID = scenarioID
|
||||
newResult.ConfigSnapshots = confSnapshots
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to PUT a non JSON body
|
||||
|
@ -304,7 +304,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
|||
newResult.Description = updatedResult.Description
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Count the number of all the results returned for scenario
|
||||
|
@ -345,7 +345,7 @@ func TestAddGetUpdateDeleteResult(t *testing.T) {
|
|||
func TestAddDeleteResultFile(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario
|
||||
|
@ -358,7 +358,7 @@ func TestAddDeleteResultFile(t *testing.T) {
|
|||
newResult.ScenarioID = scenarioID
|
||||
newResult.ConfigSnapshots = confSnapshots
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST newResult
|
||||
|
|
|
@ -22,14 +22,10 @@
|
|||
package scenario
|
||||
|
||||
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/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) {
|
||||
|
@ -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
|
||||
userID, _ := c.Get(database.UserIDCtx)
|
||||
|
||||
var u user.User
|
||||
err := u.ByID(userID.(uint))
|
||||
db := database.GetDB()
|
||||
var u database.User
|
||||
err := db.Find(&u, userID.(uint)).Error
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// get all scenarios for the user who issues the request
|
||||
db := database.GetDB()
|
||||
|
||||
var scenarios []database.Scenario
|
||||
if u.Role == "Admin" { // Admin can see all scenarios
|
||||
err = db.Order("ID asc").Find(&scenarios).Error
|
||||
|
@ -102,16 +98,16 @@ func getScenarios(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func addScenario(c *gin.Context) {
|
||||
|
||||
ok, _ := CheckPermissions(c, database.Create, "none", -1)
|
||||
ok, _ := database.CheckScenarioPermissions(c, database.Create, "none", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// ATTENTION: do not use c.GetInt (common.UserIDCtx) since userID is of type uint and not int
|
||||
userID, _ := c.Get(database.UserIDCtx)
|
||||
|
||||
var u user.User
|
||||
err := u.ByID(userID.(uint))
|
||||
db := database.GetDB()
|
||||
var u database.User
|
||||
err := db.Find(&u, userID.(uint)).Error
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -138,7 +134,7 @@ func addScenario(c *gin.Context) {
|
|||
}
|
||||
|
||||
// add user to new scenario
|
||||
err = newScenario.addUser(&(u.User))
|
||||
err = newScenario.addUser(&(u))
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -163,11 +159,14 @@ func addScenario(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var oldScenario Scenario
|
||||
oldScenario.Scenario = oldScenario_r
|
||||
|
||||
// Bind the (context) with the updateScenarioRequest struct
|
||||
var req updateScenarioRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@ -208,13 +207,13 @@ func updateScenario(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func getScenario(c *gin.Context) {
|
||||
|
||||
ok, so := CheckPermissions(c, database.Read, "path", -1)
|
||||
ok, so := database.CheckScenarioPermissions(c, database.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -231,11 +230,14 @@ func getScenario(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var so Scenario
|
||||
so.Scenario = so_r
|
||||
|
||||
err := so.delete()
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
|
@ -258,11 +260,14 @@ func deleteScenario(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
var so Scenario
|
||||
so.Scenario = so_r
|
||||
|
||||
// Find all users of scenario
|
||||
allUsers, _, err := so.getUsers()
|
||||
if helper.DBError(c, err) {
|
||||
|
@ -287,15 +292,18 @@ func getUsersOfScenario(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
var so Scenario
|
||||
so.Scenario = so_r
|
||||
|
||||
var u user.User
|
||||
err := u.ByUsername(username)
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
var u database.User
|
||||
db := database.GetDB()
|
||||
err := db.Find(&u, "Username = ?", username).Error
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -305,12 +313,12 @@ func addUserToScenario(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
err = so.addUser(&(u.User))
|
||||
err = so.addUser(&(u))
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.User})
|
||||
c.JSON(http.StatusOK, gin.H{"user": u})
|
||||
}
|
||||
|
||||
// deleteUserFromScenario godoc
|
||||
|
@ -328,15 +336,18 @@ func addUserToScenario(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
var so Scenario
|
||||
so.Scenario = so_r
|
||||
|
||||
var u user.User
|
||||
err := u.ByUsername(username)
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
var u database.User
|
||||
db := database.GetDB()
|
||||
err := db.Find(&u, "Username = ?", username).Error
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -346,5 +357,5 @@ func deleteUserFromScenario(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"user": u.User})
|
||||
c.JSON(http.StatusOK, gin.H{"user": u})
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -83,8 +82,8 @@ func (s *Scenario) addUser(u *database.User) error {
|
|||
func (s *Scenario) deleteUser(username string) error {
|
||||
db := database.GetDB()
|
||||
|
||||
var deletedUser user.User
|
||||
err := deletedUser.ByUsername(username)
|
||||
var deletedUser database.User
|
||||
err := db.Find(&deletedUser, "Username = ?", username).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -93,18 +92,18 @@ func (s *Scenario) deleteUser(username string) error {
|
|||
|
||||
if no_users > 1 {
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// There is only one associated user
|
||||
var remainingUser user.User
|
||||
var remainingUser database.User
|
||||
err = db.Model(s).Related(&remainingUser, "Users").Error
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -155,33 +154,3 @@ func (s *Scenario) delete() error {
|
|||
|
||||
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{
|
||||
RawMessage: json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`),
|
||||
},
|
||||
IsLocked: false,
|
||||
}
|
||||
|
||||
var newScenario2 = ScenarioRequest{
|
||||
|
@ -64,6 +65,7 @@ var newScenario2 = ScenarioRequest{
|
|||
StartParameters: postgres.Jsonb{
|
||||
RawMessage: json.RawMessage(`{"parameter1" : "testValue1B", "parameter2" : "testValue2B", "parameter3" : 55}`),
|
||||
},
|
||||
IsLocked: false,
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -95,10 +97,10 @@ func TestAddScenario(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as guest user
|
||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario
|
||||
|
@ -258,7 +260,7 @@ func TestUpdateScenario(t *testing.T) {
|
|||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// 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)
|
||||
|
||||
// changed locked state of scenario as admin user (should work)
|
||||
|
@ -294,7 +296,7 @@ func TestUpdateScenario(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get the updatedScenario
|
||||
|
@ -320,10 +322,10 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as admin
|
||||
token, err := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// get the length of the GET all scenarios response for admin
|
||||
|
@ -332,7 +334,7 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// authenticate as normal userB
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario1
|
||||
|
@ -342,7 +344,7 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
|||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// authenticate as normal userA
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario2
|
||||
|
@ -352,7 +354,7 @@ func TestGetAllScenariosAsAdmin(t *testing.T) {
|
|||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// authenticate as admin
|
||||
token, err = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// get the length of the GET all scenarios response again
|
||||
|
@ -367,10 +369,10 @@ func TestGetAllScenariosAsUser(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal userB
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserBCredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal userA
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario1
|
||||
|
@ -395,7 +397,7 @@ func TestGetAllScenariosAsUser(t *testing.T) {
|
|||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// authenticate as normal userB
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserBCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserBCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// get the length of the GET all scenarios response again
|
||||
|
@ -410,10 +412,10 @@ func TestDeleteScenario(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario
|
||||
|
@ -433,7 +435,7 @@ func TestDeleteScenario(t *testing.T) {
|
|||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// authenticate as guest user
|
||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Count the number of all the scenarios returned for userA
|
||||
|
@ -474,10 +476,10 @@ func TestAddUserToScenario(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario
|
||||
|
@ -491,7 +493,7 @@ func TestAddUserToScenario(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Count the number of all the users returned for newScenario
|
||||
|
@ -526,9 +528,9 @@ func TestAddUserToScenario(t *testing.T) {
|
|||
|
||||
// Compare resp to userB
|
||||
userB := UserRequest{
|
||||
Username: helper.UserB.Username,
|
||||
Mail: helper.UserB.Mail,
|
||||
Role: helper.UserB.Role,
|
||||
Username: database.UserB.Username,
|
||||
Mail: database.UserB.Mail,
|
||||
Role: database.UserB.Role,
|
||||
}
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"user": userB})
|
||||
assert.NoError(t, err)
|
||||
|
@ -551,10 +553,10 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario
|
||||
|
@ -568,7 +570,7 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as admin
|
||||
token, err = helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// set userB as inactive
|
||||
|
@ -612,7 +614,7 @@ func TestGetAllUsersOfScenario(t *testing.T) {
|
|||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Count AGAIN the number of all the users returned for newScenario
|
||||
|
@ -626,10 +628,10 @@ func TestRemoveUserFromScenario(t *testing.T) {
|
|||
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST scenarios/ $newScenario
|
||||
|
@ -649,7 +651,7 @@ func TestRemoveUserFromScenario(t *testing.T) {
|
|||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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
|
||||
userC := UserRequest{
|
||||
Username: helper.UserC.Username,
|
||||
Mail: helper.UserC.Mail,
|
||||
Role: helper.UserC.Role,
|
||||
Username: database.UserC.Username,
|
||||
Mail: database.UserC.Mail,
|
||||
Role: database.UserC.Role,
|
||||
}
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"user": userC})
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"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) {
|
||||
|
@ -55,7 +54,7 @@ func RegisterSignalEndpoints(r *gin.RouterGroup) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -111,13 +110,13 @@ func addSignal(c *gin.Context) {
|
|||
// Create the new signal from the request
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
// Add signal to component configuration
|
||||
err := newSignal.addToConfig()
|
||||
err := newSignal.AddToConfig()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"signal": newSignal.Signal})
|
||||
}
|
||||
|
@ -139,11 +138,14 @@ func addSignal(c *gin.Context) {
|
|||
// @Router /signals/{signalID} [put]
|
||||
// @Security Bearer
|
||||
func updateSignal(c *gin.Context) {
|
||||
ok, oldSignal := checkPermissions(c, database.Delete)
|
||||
ok, oldSignal_r := database.CheckSignalPermissions(c, database.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var oldSignal Signal
|
||||
oldSignal.Signal = oldSignal_r
|
||||
|
||||
var req updateSignalRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.BadRequestError(c, err.Error())
|
||||
|
@ -181,12 +183,12 @@ func updateSignal(c *gin.Context) {
|
|||
// @Router /signals/{signalID} [get]
|
||||
// @Security Bearer
|
||||
func getSignal(c *gin.Context) {
|
||||
ok, sig := checkPermissions(c, database.Delete)
|
||||
ok, sig := database.CheckSignalPermissions(c, database.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"signal": sig.Signal})
|
||||
c.JSON(http.StatusOK, gin.H{"signal": sig})
|
||||
}
|
||||
|
||||
// deleteSignal godoc
|
||||
|
@ -204,11 +206,14 @@ func getSignal(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func deleteSignal(c *gin.Context) {
|
||||
|
||||
ok, sig := checkPermissions(c, database.Delete)
|
||||
ok, sig_r := database.CheckSignalPermissions(c, database.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var sig Signal
|
||||
sig.Signal = sig_r
|
||||
|
||||
err := sig.delete()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"signal": sig.Signal})
|
||||
|
|
|
@ -23,7 +23,6 @@ package signal
|
|||
|
||||
import (
|
||||
"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 {
|
||||
|
@ -36,20 +35,20 @@ func (s *Signal) save() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (s *Signal) byID(id uint) error {
|
||||
/*func (s *Signal) byID(id uint) error {
|
||||
db := database.GetDB()
|
||||
err := db.Find(s, id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}*/
|
||||
|
||||
func (s *Signal) addToConfig() error {
|
||||
func (s *Signal) AddToConfig() error {
|
||||
db := database.GetDB()
|
||||
var err error
|
||||
var m component_configuration.ComponentConfiguration
|
||||
err = m.ByID(s.ConfigID)
|
||||
var m database.ComponentConfiguration
|
||||
err = db.Find(&m, s.ConfigID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,8 +91,8 @@ func (s *Signal) delete() error {
|
|||
|
||||
db := database.GetDB()
|
||||
var err error
|
||||
var m component_configuration.ComponentConfiguration
|
||||
err = m.ByID(s.ConfigID)
|
||||
var m database.ComponentConfiguration
|
||||
err = db.Find(&m, s.ConfigID).Error
|
||||
if err != nil {
|
||||
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) {
|
||||
|
||||
// authenticate as admin
|
||||
token, _ := helper.AuthenticateForTest(router, helper.AdminCredentials)
|
||||
token, _ := helper.AuthenticateForTest(router, database.AdminCredentials)
|
||||
|
||||
// POST $newICA
|
||||
newICA := ICRequest{
|
||||
|
@ -118,7 +118,7 @@ func addScenarioAndICAndConfig() (scenarioID uint, ICID uint, configID uint) {
|
|||
newICID, _ := helper.GetResponseID(resp)
|
||||
|
||||
// authenticate as normal user
|
||||
token, _ = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, _ = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
|
||||
// POST $newScenario
|
||||
newScenario := ScenarioRequest{
|
||||
|
@ -187,7 +187,7 @@ func TestMain(m *testing.M) {
|
|||
func TestAddSignal(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -195,12 +195,12 @@ func TestAddSignal(t *testing.T) {
|
|||
_, _, configID := addScenarioAndICAndConfig()
|
||||
|
||||
// authenticate as normal user
|
||||
_, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
_, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
newSignal1.ConfigID = configID
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// Try to Get the newSignal as user B
|
||||
|
@ -271,7 +271,7 @@ func TestAddSignal(t *testing.T) {
|
|||
func TestUpdateSignal(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -279,7 +279,7 @@ func TestUpdateSignal(t *testing.T) {
|
|||
_, _, configID := addScenarioAndICAndConfig()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST signals/ $newSignal
|
||||
|
@ -300,7 +300,7 @@ func TestUpdateSignal(t *testing.T) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as guest user
|
||||
token, err = helper.AuthenticateForTest(router, helper.GuestCredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.GuestCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to PUT with non JSON body
|
||||
|
@ -363,7 +363,7 @@ func TestUpdateSignal(t *testing.T) {
|
|||
func TestDeleteSignal(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -371,7 +371,7 @@ func TestDeleteSignal(t *testing.T) {
|
|||
_, _, configID := addScenarioAndICAndConfig()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST signals/ $newSignal
|
||||
|
@ -392,7 +392,7 @@ func TestDeleteSignal(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a IC to the DB
|
||||
|
@ -457,7 +457,7 @@ func TestGetAllInputSignalsOfConfig(t *testing.T) {
|
|||
_, _, configID := addScenarioAndICAndConfig()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// try to get all input signals
|
||||
|
|
|
@ -35,6 +35,12 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var session *helper.AMQPsession
|
||||
|
||||
func SetAMQPSession(s *helper.AMQPsession) {
|
||||
session = s
|
||||
}
|
||||
|
||||
type tokenClaims struct {
|
||||
UserID uint `json:"id"`
|
||||
Role string `json:"role"`
|
||||
|
@ -68,7 +74,7 @@ func authenticated(c *gin.Context) {
|
|||
userID, _ := c.Get(database.UserIDCtx)
|
||||
|
||||
var user User
|
||||
err := user.ByID(userID.(uint))
|
||||
err := user.byID(userID.(uint))
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -120,7 +126,8 @@ func authenticate(c *gin.Context) {
|
|||
case "internal":
|
||||
myUser, err = authenticateInternal(c)
|
||||
if err != nil {
|
||||
helper.BadRequestError(c, err.Error())
|
||||
log.Println("internal auth. failed with error: ", err.Error())
|
||||
return
|
||||
}
|
||||
case "external":
|
||||
var authExternal bool
|
||||
|
@ -128,13 +135,16 @@ func authenticate(c *gin.Context) {
|
|||
if err == nil && authExternal {
|
||||
myUser, err = authenticateExternal(c)
|
||||
if err != nil {
|
||||
helper.BadRequestError(c, err.Error())
|
||||
log.Println("external auth. failed with error: ", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
helper.BadRequestError(c, "External authentication is not activated")
|
||||
return
|
||||
}
|
||||
default:
|
||||
helper.BadRequestError(c, "Invalid authentication mechanism")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if this is an active user
|
||||
|
@ -204,7 +214,7 @@ func authenticateInternal(c *gin.Context) (User, error) {
|
|||
}
|
||||
|
||||
// Find the username in the database
|
||||
err := myUser.ByUsername(credentials.Username)
|
||||
err := myUser.byUsername(credentials.Username)
|
||||
if err != nil {
|
||||
helper.UnauthorizedError(c, "Unknown username")
|
||||
return myUser, err
|
||||
|
@ -238,7 +248,7 @@ func authenticateExternal(c *gin.Context) (User, error) {
|
|||
// preferred_username := c.Request.Header.Get("X-Forwarded-Preferred-Username")
|
||||
|
||||
// check if user already exists
|
||||
err := myUser.ByUsername(username)
|
||||
err := myUser.byUsername(username)
|
||||
|
||||
if err != nil {
|
||||
// this is the first login, create new user
|
||||
|
@ -277,7 +287,11 @@ func authenticateExternal(c *gin.Context) (User, error) {
|
|||
}
|
||||
|
||||
if groupedScenario.Duplicate {
|
||||
DuplicateScenarioForUser(&so, &myUser.User)
|
||||
|
||||
if err := <-duplicateScenarioForUser(so, &myUser.User, ""); err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
} else { // add user to scenario
|
||||
err = db.Model(&so).Association("Users").Append(&(myUser.User)).Error
|
||||
if err != nil {
|
||||
|
@ -300,257 +314,3 @@ func isAlreadyDuplicated(duplicatedName string) bool {
|
|||
|
||||
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
|
||||
var oldUser User
|
||||
err = oldUser.ByID(uint(toBeUpdatedID))
|
||||
err = oldUser.byID(uint(toBeUpdatedID))
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ func getUser(c *gin.Context) {
|
|||
}
|
||||
|
||||
var user User
|
||||
err = user.ByID(uint(id))
|
||||
err = user.byID(uint(id))
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"user": user.User})
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ func deleteUser(c *gin.Context) {
|
|||
}
|
||||
|
||||
// Check that the user exist
|
||||
err = user.ByID(uint(id))
|
||||
err = user.byID(uint(id))
|
||||
if helper.DBError(c, err) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func NewUser(username, password, mail, role string, active bool) (User, error) {
|
|||
var newUser User
|
||||
|
||||
// Check that the username is NOT taken
|
||||
err := newUser.ByUsername(username)
|
||||
err := newUser.byUsername(username)
|
||||
if err == nil {
|
||||
return newUser, &UsernameAlreadyTaken{Username: username}
|
||||
}
|
||||
|
@ -83,13 +83,13 @@ func (u *User) remove() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (u *User) ByUsername(username string) error {
|
||||
func (u *User) byUsername(username string) error {
|
||||
db := database.GetDB()
|
||||
err := db.Find(u, "Username = ?", username).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *User) ByID(id uint) error {
|
||||
func (u *User) byID(id uint) error {
|
||||
db := database.GetDB()
|
||||
err := db.Find(u, id).Error
|
||||
return err
|
||||
|
|
|
@ -41,7 +41,7 @@ func claimsToContext(c *gin.Context, claims jwt.MapClaims) error {
|
|||
|
||||
var user User
|
||||
|
||||
err := user.ByID(uint(userID))
|
||||
err := user.byID(uint(userID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -25,19 +25,33 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm/dialects/postgres"
|
||||
"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/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
|
||||
|
@ -69,6 +83,14 @@ func TestMain(m *testing.M) {
|
|||
api.Use(Authentication())
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -88,7 +110,7 @@ func TestAuthenticate(t *testing.T) {
|
|||
router.ServeHTTP(w1, req)
|
||||
assert.Equalf(t, 401, w1.Code, "Response body: \n%v\n", w1.Body)
|
||||
|
||||
malformedCredentials := helper.Credentials{
|
||||
malformedCredentials := database.Credentials{
|
||||
Username: "TEST1",
|
||||
}
|
||||
// try to authenticate with malformed credentials
|
||||
|
@ -126,7 +148,7 @@ func TestAuthenticate(t *testing.T) {
|
|||
assert.Equal(t, 401, w4.Code, w4.Body)
|
||||
|
||||
// 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)
|
||||
|
||||
}
|
||||
|
@ -140,7 +162,7 @@ func TestUserGroups(t *testing.T) {
|
|||
role := "User"
|
||||
userGroups := strings.Split("testGroup1,testGroup2", ",")
|
||||
|
||||
err := myUser.ByUsername(username)
|
||||
err := myUser.byUsername(username)
|
||||
assert.Error(t, err)
|
||||
myUser, err = NewUser(username, "", email, role, true)
|
||||
assert.NoError(t, err)
|
||||
|
@ -176,7 +198,7 @@ func TestAuthenticateQueryToken(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
@ -197,7 +219,7 @@ func TestAddGetUser(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// try to POST with non JSON body
|
||||
|
@ -321,7 +343,7 @@ func TestUsersNotAllowedActions(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// Add a user
|
||||
|
@ -380,7 +402,7 @@ func TestGetAllUsers(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// get the length of the GET all users response
|
||||
|
@ -407,7 +429,7 @@ func TestGetAllUsers(t *testing.T) {
|
|||
|
||||
assert.Equal(t, finalNumber, initialNumber+1)
|
||||
|
||||
newUserCredentials := helper.Credentials{
|
||||
newUserCredentials := database.Credentials{
|
||||
Username: newUser.Username,
|
||||
Password: newUser.Password,
|
||||
}
|
||||
|
@ -433,7 +455,7 @@ func TestModifyAddedUserAsUser(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// Add a user that will modify itself
|
||||
|
@ -588,7 +610,7 @@ func TestInvalidUserUpdate(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// Add a user
|
||||
|
@ -660,7 +682,7 @@ func TestModifyAddedUserAsAdmin(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// Add a user
|
||||
|
@ -746,7 +768,7 @@ func TestModifyAddedUserAsAdmin(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// modify newUser's Active status
|
||||
|
@ -777,7 +799,7 @@ func TestDeleteUser(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// Add a user
|
||||
|
@ -827,3 +849,492 @@ func TestDeleteUser(t *testing.T) {
|
|||
|
||||
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
|
||||
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}
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ func (r *updateUserRequest) updatedUser(callerID interface{}, role interface{},
|
|||
|
||||
if role == "Admin" { // admin has to enter admin password
|
||||
var adminUser User
|
||||
err := adminUser.ByID(callerID.(uint))
|
||||
err := adminUser.byID(callerID.(uint))
|
||||
if err != nil {
|
||||
return u, err
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
"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 RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
||||
|
@ -54,7 +53,7 @@ func RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
|||
// @Security Bearer
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -100,7 +99,7 @@ func addWidget(c *gin.Context) {
|
|||
newWidget := req.createWidget()
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
@ -129,11 +128,14 @@ func addWidget(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func updateWidget(c *gin.Context) {
|
||||
|
||||
ok, oldWidget := CheckPermissions(c, database.Update, -1)
|
||||
ok, oldWidget_r := database.CheckWidgetPermissions(c, database.Update, -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var oldWidget Widget
|
||||
oldWidget.Widget = oldWidget_r
|
||||
|
||||
var req updateWidgetRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.BadRequestError(c, err.Error())
|
||||
|
@ -172,12 +174,12 @@ func updateWidget(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func getWidget(c *gin.Context) {
|
||||
|
||||
ok, w := CheckPermissions(c, database.Read, -1)
|
||||
ok, w := database.CheckWidgetPermissions(c, database.Read, -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"widget": w.Widget})
|
||||
c.JSON(http.StatusOK, gin.H{"widget": w})
|
||||
}
|
||||
|
||||
// deleteWidget godoc
|
||||
|
@ -195,11 +197,14 @@ func getWidget(c *gin.Context) {
|
|||
// @Security Bearer
|
||||
func deleteWidget(c *gin.Context) {
|
||||
|
||||
ok, w := CheckPermissions(c, database.Delete, -1)
|
||||
ok, w_r := database.CheckWidgetPermissions(c, database.Delete, -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var w Widget
|
||||
w.Widget = w_r
|
||||
|
||||
err := w.delete()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"widget": w.Widget})
|
||||
|
|
|
@ -23,7 +23,6 @@ package widget
|
|||
|
||||
import (
|
||||
"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 {
|
||||
|
@ -47,8 +46,8 @@ func (w *Widget) ByID(id uint) error {
|
|||
|
||||
func (w *Widget) addToDashboard() error {
|
||||
db := database.GetDB()
|
||||
var dab dashboard.Dashboard
|
||||
err := dab.ByID(uint(w.DashboardID))
|
||||
var dab database.Dashboard
|
||||
err := db.Find(&dab, uint(w.DashboardID)).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -59,7 +58,7 @@ func (w *Widget) addToDashboard() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// associate dashboard with simulation
|
||||
// associate widget with dashboard
|
||||
err = db.Model(&dab).Association("Widgets").Append(w).Error
|
||||
|
||||
return err
|
||||
|
@ -89,8 +88,8 @@ func (w *Widget) update(modifiedWidget Widget) error {
|
|||
func (w *Widget) delete() error {
|
||||
|
||||
db := database.GetDB()
|
||||
var dab dashboard.Dashboard
|
||||
err := dab.ByID(w.DashboardID)
|
||||
var dab database.Dashboard
|
||||
err := db.Find(&dab, uint(w.DashboardID)).Error
|
||||
if err != nil {
|
||||
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) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, dashboardID := addScenarioAndDashboard(token)
|
||||
|
||||
newWidget.DashboardID = dashboardID
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, dashboardID := addScenarioAndDashboard(token)
|
||||
|
@ -259,7 +259,7 @@ func TestUpdateWidget(t *testing.T) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// try to PUT non JSON body
|
||||
|
@ -322,10 +322,10 @@ func TestUpdateWidget(t *testing.T) {
|
|||
func TestDeleteWidget(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, dashboardID := addScenarioAndDashboard(token)
|
||||
|
@ -342,7 +342,7 @@ func TestDeleteWidget(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Count the number of all the widgets returned for dashboard
|
||||
|
@ -382,16 +382,16 @@ func TestDeleteWidget(t *testing.T) {
|
|||
func TestGetAllWidgetsOfDashboard(t *testing.T) {
|
||||
database.DropTables()
|
||||
database.MigrateModels()
|
||||
assert.NoError(t, helper.AddTestUsers())
|
||||
assert.NoError(t, database.AddTestUsers())
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err := helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, dashboardID := addScenarioAndDashboard(token)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// authenticate as normal user
|
||||
token, err = helper.AuthenticateForTest(router, helper.UserACredentials)
|
||||
token, err = helper.AuthenticateForTest(router, database.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Count the number of all the widgets returned for dashboard
|
||||
|
|
23
start.go
23
start.go
|
@ -23,6 +23,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
||||
"log"
|
||||
"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/helper"
|
||||
"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"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/zpatrick/go-config"
|
||||
|
@ -121,16 +123,21 @@ func main() {
|
|||
|
||||
if amqpHost != "" {
|
||||
// create amqp URL based on username, password and host
|
||||
amqpURL := "amqp://" + amqpUser + ":" + amqpPass + "@" + amqpHost
|
||||
err = infrastructure_component.StartAMQP(amqpURL, api)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
amqpurl := "amqp://" + amqpUser + ":" + amqpPass + "@" + amqpHost
|
||||
session := helper.NewAMQPSession("villas-amqp-session", amqpurl, "villas", infrastructure_component.ProcessMessage)
|
||||
healthz.SetAMQPSession(session) // healthz needs to know the amqp session to check the health of the backend
|
||||
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
|
||||
err = helper.SendPing("")
|
||||
if err != nil {
|
||||
log.Println("error sending ping action via AMQP: ", err)
|
||||
for {
|
||||
if session.IsReady {
|
||||
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