diff --git a/database/database.go b/database/database.go index 0ed6435..537b065 100644 --- a/database/database.go +++ b/database/database.go @@ -176,3 +176,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,required"` + Password string `json:"password,required"` +} + +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, +} diff --git a/database/permissions.go b/database/permissions.go new file mode 100644 index 0000000..e3440d7 --- /dev/null +++ b/database/permissions.go @@ -0,0 +1,301 @@ +/** Database package, roles. +* +* @author Sonja Happ +* @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 . +*********************************************************************************/ +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 + } + + hasAccess := false + u := User{} + + err = db.Find(&u, userID.(uint)).Error + if err != nil { + hasAccess = false + } + + if u.Role == "Admin" { + hasAccess = true + } + + scenarioUser := User{} + err = db.Order("ID asc").Model(&so).Where("ID = ?", userID.(uint)).Related(&scenarioUser, "Users").Error + if err != nil { + hasAccess = false + } + + if !scenarioUser.Active { + hasAccess = false + } else if so.IsLocked && operation != Read { + hasAccess = false + } else { + hasAccess = true + } + + if hasAccess == false { + helper.UnprocessableEntityError(c, "Access denied (user has no access or scenario is locked).") + return false, so + } + + 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 +} diff --git a/database/roles.go b/database/roles.go index 7beeac1..dfe9b4a 100644 --- a/database/roles.go +++ b/database/roles.go @@ -23,7 +23,6 @@ package database import ( "fmt" - "github.com/gin-gonic/gin" ) diff --git a/helper/test_utilities.go b/helper/test_utilities.go index ed655db..9a29b4a 100644 --- a/helper/test_utilities.go +++ b/helper/test_utilities.go @@ -25,10 +25,8 @@ import ( "bytes" "encoding/json" "fmt" - "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" "log" "net/http" "net/http/httptest" @@ -41,28 +39,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"` @@ -72,31 +48,6 @@ type UserRequest struct { Active string `json:"active,omitempty"` } -type Credentials struct { - Username string `json:"username,required"` - Password string `json:"password,required"` -} - -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 ############################ // ############################################################################ @@ -314,20 +265,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 -} diff --git a/routes/component-configuration/config_endpoints.go b/routes/component-configuration/config_endpoints.go index b3c40c3..070d0ba 100644 --- a/routes/component-configuration/config_endpoints.go +++ b/routes/component-configuration/config_endpoints.go @@ -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}) diff --git a/routes/component-configuration/config_methods.go b/routes/component-configuration/config_methods.go index 6f416a8..2c03b2e 100644 --- a/routes/component-configuration/config_methods.go +++ b/routes/component-configuration/config_methods.go @@ -23,7 +23,7 @@ package component_configuration import ( "git.rwth-aachen.de/acs/public/villas/web-backend-go/database" - "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario" + "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/signal" "log" ) @@ -48,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 } @@ -119,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 } @@ -185,3 +185,53 @@ func (m *ComponentConfiguration) delete() error { return nil } + +func (m *ComponentConfiguration) Duplicate(scenarioID uint, icIds map[uint]string, signalMap *map[uint]uint) error { + + db := database.GetDB() + + var dup 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 + err := dup.addToScenario() + if err != nil { + return err + } + + // duplication of signals + var sigs []signal.Signal + err = db.Order("ID asc").Model(&m).Related(&sigs, "OutputMapping").Error + smap := *signalMap + for _, s := range sigs { + var sigDup signal.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 + err = sigDup.AddToConfig() + if err != nil { + return err + } + + smap[s.ID] = sigDup.ID + } + + return nil +} diff --git a/routes/component-configuration/config_middleware.go b/routes/component-configuration/config_middleware.go deleted file mode 100644 index 8f19c41..0000000 --- a/routes/component-configuration/config_middleware.go +++ /dev/null @@ -1,59 +0,0 @@ -/** component_configuration package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ -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 -} diff --git a/routes/component-configuration/config_test.go b/routes/component-configuration/config_test.go index f99252e..607564e 100644 --- a/routes/component-configuration/config_test.go +++ b/routes/component-configuration/config_test.go @@ -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{ @@ -114,7 +114,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{ @@ -168,7 +168,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 @@ -178,7 +178,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 @@ -189,7 +189,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 @@ -199,7 +199,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 @@ -238,7 +238,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 @@ -254,7 +254,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 @@ -262,7 +262,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 @@ -283,7 +283,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 @@ -294,7 +294,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 @@ -305,7 +305,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 @@ -357,7 +357,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 @@ -367,7 +367,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 @@ -381,7 +381,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 @@ -392,7 +392,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 @@ -421,7 +421,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 @@ -431,7 +431,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 @@ -448,7 +448,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 diff --git a/routes/dashboard/dashboard_endpoints.go b/routes/dashboard/dashboard_endpoints.go index 7649a0c..096a7b6 100644 --- a/routes/dashboard/dashboard_endpoints.go +++ b/routes/dashboard/dashboard_endpoints.go @@ -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}) diff --git a/routes/dashboard/dashboard_methods.go b/routes/dashboard/dashboard_methods.go index 913b93c..20928d0 100644 --- a/routes/dashboard/dashboard_methods.go +++ b/routes/dashboard/dashboard_methods.go @@ -23,7 +23,8 @@ 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" + "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget" + "log" ) type Dashboard struct { @@ -47,8 +48,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 +82,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 } @@ -107,3 +108,35 @@ func (d *Dashboard) delete() error { return err } + +func (d *Dashboard) Duplicate(scenarioID uint, signalMap map[uint]uint) error { + + var duplicateD Dashboard + duplicateD.Grid = d.Grid + duplicateD.Name = d.Name + duplicateD.ScenarioID = scenarioID + duplicateD.Height = d.Height + err := duplicateD.addToScenario() + + if err != nil { + return err + } + + // add widgets to duplicated dashboard + var widgets []widget.Widget + db := database.GetDB() + 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 = w.Duplicate(duplicateD.ID, signalMap) + if err != nil { + log.Printf("error creating duplicate for widget %d: %s", w.ID, err) + continue + } + } + + return nil +} diff --git a/routes/dashboard/dashboard_middleware.go b/routes/dashboard/dashboard_middleware.go deleted file mode 100644 index 2b19087..0000000 --- a/routes/dashboard/dashboard_middleware.go +++ /dev/null @@ -1,59 +0,0 @@ -/** Dashboard package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ -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 -} diff --git a/routes/dashboard/dashboard_test.go b/routes/dashboard/dashboard_test.go index 512eac0..2b12d16 100644 --- a/routes/dashboard/dashboard_test.go +++ b/routes/dashboard/dashboard_test.go @@ -107,10 +107,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) @@ -166,7 +166,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 @@ -187,10 +187,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) @@ -212,7 +212,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 @@ -223,7 +223,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, @@ -261,10 +261,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) @@ -281,7 +281,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 @@ -291,7 +291,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 @@ -327,10 +327,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) @@ -372,7 +372,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 diff --git a/routes/file/file_endpoints.go b/routes/file/file_endpoints.go index 3d7d962..d78bc06 100644 --- a/routes/file/file_endpoints.go +++ b/routes/file/file_endpoints.go @@ -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}) diff --git a/routes/file/file_methods.go b/routes/file/file_methods.go index 788d123..0d60a75 100644 --- a/routes/file/file_methods.go +++ b/routes/file/file_methods.go @@ -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" @@ -140,8 +139,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 } @@ -219,8 +218,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 } @@ -248,3 +247,38 @@ func (f *File) Delete() error { return err } + +func (f *File) Duplicate(scenarioID uint) error { + + var dup 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 + err := dup.save() + if err != nil { + return err + } + + // Create association of duplicate file to scenario ID of duplicate file + db := database.GetDB() + + 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 +} diff --git a/routes/file/file_middleware.go b/routes/file/file_middleware.go deleted file mode 100644 index 897fa52..0000000 --- a/routes/file/file_middleware.go +++ /dev/null @@ -1,61 +0,0 @@ -/** File package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ -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 -} diff --git a/routes/file/file_test.go b/routes/file/file_test.go index 948ebda..8d8a3ec 100644 --- a/routes/file/file_test.go +++ b/routes/file/file_test.go @@ -53,10 +53,10 @@ type ScenarioRequest struct { func addScenario() (scenarioID uint) { // authenticate as admin - token, _ := helper.AuthenticateForTest(router, helper.AdminCredentials) + token, _ := 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 @@ -318,14 +318,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 @@ -385,7 +385,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 @@ -396,7 +396,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 @@ -412,7 +412,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 @@ -423,7 +423,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 @@ -450,14 +450,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 @@ -468,7 +468,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 diff --git a/routes/infrastructure-component/ic_endpoints.go b/routes/infrastructure-component/ic_endpoints.go index 3471e5b..a6c40f2 100644 --- a/routes/infrastructure-component/ic_endpoints.go +++ b/routes/infrastructure-component/ic_endpoints.go @@ -86,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 } @@ -147,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 @@ -196,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 @@ -219,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 @@ -258,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) { @@ -287,7 +296,7 @@ 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 } diff --git a/routes/infrastructure-component/ic_middleware.go b/routes/infrastructure-component/ic_middleware.go deleted file mode 100644 index a5c7d5a..0000000 --- a/routes/infrastructure-component/ic_middleware.go +++ /dev/null @@ -1,55 +0,0 @@ -/** InfrastructureComponent package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ -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 -} diff --git a/routes/infrastructure-component/ic_test.go b/routes/infrastructure-component/ic_test.go index 844df34..4542c56 100644 --- a/routes/infrastructure-component/ic_test.go +++ b/routes/infrastructure-component/ic_test.go @@ -148,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 @@ -213,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 @@ -232,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 @@ -321,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 @@ -338,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 @@ -354,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 @@ -431,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 @@ -448,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 @@ -463,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 @@ -495,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 @@ -509,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 @@ -534,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 @@ -556,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 @@ -605,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 @@ -679,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 diff --git a/routes/register.go b/routes/register.go index 25cc977..9308caa 100644 --- a/routes/register.go +++ b/routes/register.go @@ -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, } diff --git a/routes/result/result_endpoints.go b/routes/result/result_endpoints.go index 71d62c1..90e8740 100644 --- a/routes/result/result_endpoints.go +++ b/routes/result/result_endpoints.go @@ -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 } diff --git a/routes/result/result_methods.go b/routes/result/result_methods.go index cad7c55..5806e2c 100644 --- a/routes/result/result_methods.go +++ b/routes/result/result_methods.go @@ -25,7 +25,6 @@ package result 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" "log" ) @@ -50,8 +49,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 } @@ -84,8 +83,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 } diff --git a/routes/result/result_middleware.go b/routes/result/result_middleware.go deleted file mode 100644 index 7656087..0000000 --- a/routes/result/result_middleware.go +++ /dev/null @@ -1,59 +0,0 @@ -/** Result package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ - -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 -} diff --git a/routes/result/result_test.go b/routes/result/result_test.go index 9d4aeff..5034486 100644 --- a/routes/result/result_test.go +++ b/routes/result/result_test.go @@ -70,10 +70,10 @@ var newResult = ResultRequest{ func addScenario() (scenarioID uint) { // authenticate as admin - token, _ := helper.AuthenticateForTest(router, helper.AdminCredentials) + token, _ := 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 @@ -152,7 +152,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 @@ -168,7 +168,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 @@ -178,7 +178,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 @@ -189,7 +189,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 @@ -234,7 +234,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 @@ -259,7 +259,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 @@ -270,7 +270,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 @@ -300,7 +300,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 @@ -311,7 +311,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 @@ -341,7 +341,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 @@ -352,7 +352,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 diff --git a/routes/scenario/scenario_endpoints.go b/routes/scenario/scenario_endpoints.go index c597ccc..7939b15 100644 --- a/routes/scenario/scenario_endpoints.go +++ b/routes/scenario/scenario_endpoints.go @@ -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}) } diff --git a/routes/scenario/scenario_methods.go b/routes/scenario/scenario_methods.go index 49ea335..755e3d9 100644 --- a/routes/scenario/scenario_methods.go +++ b/routes/scenario/scenario_methods.go @@ -24,8 +24,13 @@ package scenario 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" + 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" "github.com/jinzhu/gorm" + "log" + "time" ) type Scenario struct { @@ -82,8 +87,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 } @@ -92,18 +97,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 remainingUser.Username == username { // if the remaining user is the one to be deleted @@ -152,32 +157,160 @@ func (s *Scenario) delete() error { return nil } -func (s *Scenario) checkAccess(userID uint, operation database.CRUD) bool { +func (s *Scenario) DuplicateScenarioForUser(user *database.User) <-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 infrastructure_component.InfrastructureComponent + err = ic.ByID(icID) + + if err != nil { + errs <- fmt.Errorf("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 := ic.RequestICcreateAMQPsimpleManager(user.Username) + 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 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 { + err := s.duplicateScenario(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 := 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 { + errs <- fmt.Errorf("Error looking up duplicated IC: %s", err) + } else { + icsToWaitFor-- + uuid = "" + } + } + } + + errs <- fmt.Errorf("ALERT! Timed out while waiting for IC duplication, scenario not properly duplicated") + close(errs) + + }() + + return errs +} + +func (s *Scenario) duplicateScenario(icIds map[uint]string, user *database.User) error { db := database.GetDB() - u := database.User{} - err := db.Find(&u, userID).Error + var duplicateSo Scenario + duplicateSo.Name = s.Name + ` ` + user.Username + duplicateSo.StartParameters.RawMessage = s.StartParameters.RawMessage + err := duplicateSo.save() if err != nil { - return false + log.Printf("Could not create duplicate of scenario %d", s.ID) + return err } - if u.Role == "Admin" { - return true - } - - scenarioUser := database.User{} - err = db.Order("ID asc").Model(s).Where("ID = ?", userID).Related(&scenarioUser, "Users").Error + // associate user to new scenario + err = duplicateSo.addUser(user) if err != nil { - return false + 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 []file.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 = f.Duplicate(duplicateSo.ID) + if err != nil { + log.Print("error creating duplicate file %d: %s", f.ID, err) + continue + } } - if !scenarioUser.Active { - return false - } else if s.IsLocked && operation != database.Read { - return false + var configs []component_configuration.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 = c.Duplicate(duplicateSo.ID, icIds, &signalMap) + //err = duplicateComponentConfig(&c, duplicateSo, icIds, userName, &signalMap) + if err != nil { + log.Printf("Error duplicating component config %d: %s", c.ID, err) + continue + } + } } else { - return true + return err } + var dabs []dashboard.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 = dab.Duplicate(duplicateSo.ID, signalMap) + if err != nil { + log.Printf("Error duplicating dashboard %d: %s", dab.ID, err) + continue + } + } + + return err } diff --git a/routes/scenario/scenario_middleware.go b/routes/scenario/scenario_middleware.go deleted file mode 100644 index d908e03..0000000 --- a/routes/scenario/scenario_middleware.go +++ /dev/null @@ -1,64 +0,0 @@ -/** Scenario package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ -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) == false { - helper.UnprocessableEntityError(c, "Access denied (user has no access or scenario is locked).") - return false, so - } - - return true, so -} diff --git a/routes/scenario/scenario_test.go b/routes/scenario/scenario_test.go index 8580e49..4ebc615 100644 --- a/routes/scenario/scenario_test.go +++ b/routes/scenario/scenario_test.go @@ -91,10 +91,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 @@ -147,7 +147,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 @@ -158,7 +158,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 @@ -169,7 +169,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 @@ -183,10 +183,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 @@ -254,7 +254,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) @@ -290,7 +290,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 @@ -316,10 +316,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 @@ -328,7 +328,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 @@ -338,7 +338,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 @@ -348,7 +348,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 @@ -363,10 +363,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 @@ -381,7 +381,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 @@ -391,7 +391,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 @@ -406,10 +406,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 @@ -429,7 +429,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 @@ -440,7 +440,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 @@ -470,10 +470,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 @@ -487,7 +487,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 @@ -505,7 +505,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 @@ -522,9 +522,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) @@ -547,10 +547,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 @@ -564,7 +564,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 @@ -575,7 +575,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 @@ -597,7 +597,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 @@ -608,7 +608,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 @@ -622,10 +622,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 @@ -645,7 +645,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 @@ -656,7 +656,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 @@ -673,9 +673,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) @@ -708,3 +708,13 @@ func TestRemoveUserFromScenario(t *testing.T) { assert.Equalf(t, 404, code, "Response body: \n%v\n", resp) } + +func TestDuplicateScenarioForUser(t *testing.T) { + + database.DropTables() + database.MigrateModels() + assert.NoError(t, database.AddTestUsers()) + + // TODO test duplicate scenario for user function here!! + +} diff --git a/routes/signal/signal_endpoints.go b/routes/signal/signal_endpoints.go index ce09561..9b65301 100644 --- a/routes/signal/signal_endpoints.go +++ b/routes/signal/signal_endpoints.go @@ -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}) diff --git a/routes/signal/signal_methods.go b/routes/signal/signal_methods.go index fb0a269..d767216 100644 --- a/routes/signal/signal_methods.go +++ b/routes/signal/signal_methods.go @@ -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 { @@ -45,11 +44,11 @@ func (s *Signal) byID(id uint) error { 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 } diff --git a/routes/signal/signal_middleware.go b/routes/signal/signal_middleware.go deleted file mode 100644 index 8f80b8d..0000000 --- a/routes/signal/signal_middleware.go +++ /dev/null @@ -1,59 +0,0 @@ -/** Signal package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ -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 -} diff --git a/routes/signal/signal_test.go b/routes/signal/signal_test.go index e48b775..fdb52d3 100644 --- a/routes/signal/signal_test.go +++ b/routes/signal/signal_test.go @@ -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{ @@ -114,7 +114,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{ @@ -181,7 +181,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 @@ -189,12 +189,12 @@ func TestAddSignal(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) 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 @@ -205,7 +205,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 @@ -251,7 +251,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 @@ -265,7 +265,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 @@ -273,7 +273,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 @@ -294,7 +294,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 @@ -305,7 +305,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 @@ -316,7 +316,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 @@ -357,7 +357,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 @@ -365,7 +365,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 @@ -386,7 +386,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 @@ -397,7 +397,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 @@ -443,7 +443,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 @@ -451,7 +451,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 @@ -526,7 +526,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 diff --git a/routes/user/authenticate_endpoint.go b/routes/user/authenticate_endpoint.go index 807d061..c86494a 100644 --- a/routes/user/authenticate_endpoint.go +++ b/routes/user/authenticate_endpoint.go @@ -23,6 +23,7 @@ package user import ( "fmt" + "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario" "log" "net/http" "strings" @@ -69,7 +70,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 } @@ -205,7 +206,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 @@ -239,7 +240,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 @@ -262,7 +263,7 @@ func authenticateExternal(c *gin.Context) (User, error) { for _, group := range groups { if groupedArr, ok := configuration.ScenarioGroupMap[group]; ok { for _, groupedScenario := range groupedArr { - var so database.Scenario + var so scenario.Scenario err := db.Find(&so, groupedScenario.Scenario).Error if err != nil { log.Printf(`Cannot find scenario %s (id=%d) for adding/duplication. @@ -278,9 +279,11 @@ func authenticateExternal(c *gin.Context) (User, error) { } if groupedScenario.Duplicate { - if err := <-duplicateScenarioForUser(&so, &myUser.User); err != nil { + + if err := <-so.DuplicateScenarioForUser(&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 { diff --git a/routes/user/user_endpoints.go b/routes/user/user_endpoints.go index 3e0b03a..830048b 100644 --- a/routes/user/user_endpoints.go +++ b/routes/user/user_endpoints.go @@ -179,7 +179,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 } @@ -242,7 +242,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}) } @@ -276,7 +276,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 } diff --git a/routes/user/user_methods.go b/routes/user/user_methods.go index db22eb7..5597534 100644 --- a/routes/user/user_methods.go +++ b/routes/user/user_methods.go @@ -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, fmt.Errorf("Username is already taken") } @@ -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 diff --git a/routes/user/user_middleware.go b/routes/user/user_middleware.go index 8ff4285..3f1830e 100644 --- a/routes/user/user_middleware.go +++ b/routes/user/user_middleware.go @@ -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 } diff --git a/routes/user/user_test.go b/routes/user/user_test.go index c109cba..bf01122 100644 --- a/routes/user/user_test.go +++ b/routes/user/user_test.go @@ -88,7 +88,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 +126,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 +140,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 +176,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 +197,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 +321,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 +380,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 +407,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 +433,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 +588,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 @@ -661,7 +661,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 @@ -747,7 +747,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 @@ -778,7 +778,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 diff --git a/routes/user/user_validators.go b/routes/user/user_validators.go index b7da698..dabdb57 100644 --- a/routes/user/user_validators.go +++ b/routes/user/user_validators.go @@ -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, fmt.Errorf("Username is alreaday taken") } @@ -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 } diff --git a/routes/widget/widget_endpoints.go b/routes/widget/widget_endpoints.go index e8e78de..2ebb35c 100644 --- a/routes/widget/widget_endpoints.go +++ b/routes/widget/widget_endpoints.go @@ -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}) diff --git a/routes/widget/widget_methods.go b/routes/widget/widget_methods.go index ff58c6a..0eb7d98 100644 --- a/routes/widget/widget_methods.go +++ b/routes/widget/widget_methods.go @@ -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 } @@ -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 } @@ -106,3 +105,26 @@ func (w *Widget) delete() error { return err } + +func (w *Widget) Duplicate(dashboardID uint, signalMap map[uint]uint) error { + var duplicateW 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)])) + } + + err := duplicateW.addToDashboard() + return err +} diff --git a/routes/widget/widget_middleware.go b/routes/widget/widget_middleware.go deleted file mode 100644 index c66ee09..0000000 --- a/routes/widget/widget_middleware.go +++ /dev/null @@ -1,64 +0,0 @@ -/** Widget package, middleware. -* -* @author Sonja Happ -* @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 . -*********************************************************************************/ -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 -} diff --git a/routes/widget/widget_test.go b/routes/widget/widget_test.go index e0a987c..959916a 100644 --- a/routes/widget/widget_test.go +++ b/routes/widget/widget_test.go @@ -145,17 +145,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 @@ -166,7 +166,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 @@ -212,7 +212,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 @@ -226,10 +226,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) @@ -257,7 +257,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 @@ -268,7 +268,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 @@ -279,7 +279,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 @@ -320,10 +320,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) @@ -340,7 +340,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 @@ -351,7 +351,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 @@ -380,16 +380,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 @@ -400,7 +400,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