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:
Sonja Happ 2021-10-26 12:05:34 +02:00
commit 0269e31fa6
52 changed files with 2164 additions and 1653 deletions

View file

@ -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
View 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
}

View file

@ -23,7 +23,6 @@ package database
import (
"fmt"
"github.com/gin-gonic/gin"
)

View file

@ -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
View 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
}

View file

@ -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
}

View file

@ -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})

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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})

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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})

View 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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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)

View file

@ -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")
}
}

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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(&params)
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)

View file

@ -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,
}

View file

@ -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()

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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})
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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)

View file

@ -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})

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View 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")
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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})

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}
}
}