From 05a70f80f6a7e299795f1b91c616d5261842e51b Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 31 Jul 2019 09:37:12 +0200 Subject: [PATCH 1/5] add json tags to all models --- common/models.go | 134 ++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/common/models.go b/common/models.go index d2b283a..819132b 100644 --- a/common/models.go +++ b/common/models.go @@ -1,83 +1,95 @@ package common import ( - "github.com/jinzhu/gorm" "github.com/jinzhu/gorm/dialects/postgres" + "time" ) +// Fields shared by all models (same as gorm.model but including proper json tags) +type commonModelFields struct { + // ID of the model (primary key in DB) + ID uint `gorm:"primary_key",json:"id"` + // Time the model object is created + CreatedAt time.Time `json:"-"` + // Time of the last update of the model object + UpdatedAt time.Time `json:"-"` + // Time the model is deleted + DeletedAt *time.Time `json:"-"` +} + // User data model type User struct { - gorm.Model + commonModelFields // Username of user - Username string `gorm:"unique;not null"` + Username string `gorm:"unique;not null",json:"username"` // Password of user - Password string `gorm:"not null"` + Password string `gorm:"not null",json:"-"` // Mail of user - Mail string `gorm:"default:''"` + Mail string `gorm:"default:''",json:"mail"` // Role of user - Role string `gorm:"default:'user'"` + Role string `gorm:"default:'user'",json:"role"` // Scenarios to which user has access - Scenarios []*Scenario `gorm:"many2many:user_scenarios"` + Scenarios []*Scenario `gorm:"many2many:user_scenarios",json:"-"` } // Scenario data model type Scenario struct { - gorm.Model + commonModelFields // Name of scenario - Name string `gorm:"not null"` + Name string `gorm:"not null",json:"name"` // Running state of scenario - Running bool `gorm:"default:false"` + Running bool `gorm:"default:false",json:"running"` // Start parameters of scenario as JSON - StartParameters postgres.Jsonb + StartParameters postgres.Jsonb `json:"startParameters"` // Users that have access to the scenario - Users []*User `gorm:"not null;many2many:user_scenarios"` + Users []*User `gorm:"not null;many2many:user_scenarios",json:"-"` // SimulationModels that belong to the scenario - SimulationModels []SimulationModel `gorm:"foreignkey:ScenarioID"` + SimulationModels []SimulationModel `gorm:"foreignkey:ScenarioID",json:"-"` // Dashboards that belong to the Scenario - Dashboards []Dashboard `gorm:"foreignkey:ScenarioID"` + Dashboards []Dashboard `gorm:"foreignkey:ScenarioID",json:"-"` } // SimulationModel data model type SimulationModel struct { - gorm.Model + commonModelFields // Name of simulation model - Name string `gorm:"not null"` + Name string `gorm:"not null",json:"name"` // Number of output signals - OutputLength int `gorm:"default:1"` + OutputLength int `gorm:"default:1",json:"outputLength"` // Number of input signals - InputLength int `gorm:"default:1"` + InputLength int `gorm:"default:1",json:"inputLength"` // Start parameters of simulation model as JSON - StartParameters postgres.Jsonb + StartParameters postgres.Jsonb `json:"startParameters"` // ID of Scenario to which simulation model belongs - ScenarioID uint + ScenarioID uint `json:"scenarioID"` // ID of simulator associated with simulation model - SimulatorID uint + SimulatorID uint `json:"simulatorID"` // Mapping of output signals of the simulation model, order of signals is important - OutputMapping []Signal `gorm:"foreignkey:SimulationModelID"` + OutputMapping []Signal `gorm:"foreignkey:SimulationModelID",json:"-"` // Mapping of input signals of the simulation model, order of signals is important - InputMapping []Signal `gorm:"foreignkey:SimulationModelID"` + InputMapping []Signal `gorm:"foreignkey:SimulationModelID",json:"-"` // Files of simulation model (can be CIM and other simulation model file formats) - Files []File `gorm:"foreignkey:SimulationModelID"` + Files []File `gorm:"foreignkey:SimulationModelID",json:"-"` } // Signal data model type Signal struct { - gorm.Model + commonModelFields // Name of Signal - Name string + Name string `json:"name"` // Unit of Signal - Unit string + Unit string `json:"unit"` // Index of the Signal in the mapping - Index uint + Index uint `json:"index"` // Direction of the signal (in or out) - Direction string + Direction string `json:"direction"` // ID of simulation model - SimulationModelID uint + SimulationModelID uint `json:"simulationModelID"` } // Simulator data model type Simulator struct { - gorm.Model + commonModelFields // UUID of the simulator UUID string `gorm:"not null",json:"uuid"` // Host if the simulator @@ -95,74 +107,74 @@ type Simulator struct { // Raw properties of simulator as JSON string RawProperties postgres.Jsonb `json:"rawProperties"` // SimulationModels in which the simulator is used - SimulationModels []SimulationModel `gorm:"foreignkey:SimulatorID"` + SimulationModels []SimulationModel `gorm:"foreignkey:SimulatorID",json:"-"` } // Dashboard data model type Dashboard struct { - gorm.Model + commonModelFields // Name of dashboard - Name string `gorm:"not null"` + Name string `gorm:"not null",json:"name"` // Grid of dashboard - Grid int `gorm:"default:15"` + Grid int `gorm:"default:15",json:"grid"` // ID of scenario to which dashboard belongs - ScenarioID uint + ScenarioID uint `json:"scenarioID"` // Widgets that belong to dashboard - Widgets []Widget `gorm:"foreignkey:DashboardID"` + Widgets []Widget `gorm:"foreignkey:DashboardID",json:"-"` } // Widget data model type Widget struct { - gorm.Model + commonModelFields // Name of widget - Name string `gorm:"not null"` + Name string `gorm:"not null",json:"name"` // Type of widget - Type string `gorm:"not null"` + Type string `gorm:"not null",json:"type"` // Width of widget - Width uint `gorm:"not null"` + Width uint `gorm:"not null",json:"width"` // Height of widget - Height uint `gorm:"not null"` + Height uint `gorm:"not null",json:"height"` // Minimal width of widget - MinWidth uint `gorm:"not null"` + MinWidth uint `gorm:"not null",json:"minWidth"` // Minimal height of widget - MinHeight uint `gorm:"not null"` + MinHeight uint `gorm:"not null",json:"minHeight"` // X position of widget - X int `gorm:"not null"` + X int `gorm:"not null",json:"x"` // Y position of widget - Y int `gorm:"not null"` + Y int `gorm:"not null",json:"y"` // Z position of widget - Z int `gorm:"not null"` + Z int `gorm:"not null",json:"z"` // Locked state of widget - IsLocked bool `gorm:"default:false"` + IsLocked bool `gorm:"default:false",json:"isLocked"` // Custom properties of widget as JSON string - CustomProperties postgres.Jsonb + CustomProperties postgres.Jsonb `json:"customProperties"` // ID of dashboard to which widget belongs - DashboardID uint + DashboardID uint `json:"dashboardID"` // Files that belong to widget (for example images) - Files []File `gorm:"foreignkey:WidgetID"` + Files []File `gorm:"foreignkey:WidgetID",json:"-"` } // File data model type File struct { - gorm.Model + commonModelFields // Name of file - Name string `gorm:"not null"` + Name string `gorm:"not null",json:"name"` // Type of file (MIME type) - Type string + Type string `json:"type"` // Size of file (in byte) - Size uint + Size uint `json:"size"` // Height of image (only needed in case of image) - ImageHeight uint + ImageHeight uint `json:"imageHeight"` // Width of image (only needed in case of image) - ImageWidth uint + ImageWidth uint `json:"imageWidth"` // Last modification time of file - Date string + Date string `json:"date"` // ID of model to which file belongs - SimulationModelID uint + SimulationModelID uint `json:"simulationModelID"` // ID of widget to which file belongs - WidgetID uint + WidgetID uint `json:"widgetID"` // File itself - FileData []byte `gorm:"column:FileData"` + FileData []byte `gorm:"column:FileData",json:"-"` } // Credentials type (not for DB) From a4a6ab077085f9065c8c6adc5ed8de2dd335f6e6 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 31 Jul 2019 09:37:51 +0200 Subject: [PATCH 2/5] rename user validators file --- routes/user/{userValidators.go => user_validators.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename routes/user/{userValidators.go => user_validators.go} (100%) diff --git a/routes/user/userValidators.go b/routes/user/user_validators.go similarity index 100% rename from routes/user/userValidators.go rename to routes/user/user_validators.go From ee708e1b7b58491b37d1d6ebe944a5c387fcfb16 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 31 Jul 2019 12:56:40 +0200 Subject: [PATCH 3/5] Use capital letter for CommonModelFields type --- common/models.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/common/models.go b/common/models.go index 819132b..4256f71 100644 --- a/common/models.go +++ b/common/models.go @@ -6,7 +6,7 @@ import ( ) // Fields shared by all models (same as gorm.model but including proper json tags) -type commonModelFields struct { +type CommonModelFields struct { // ID of the model (primary key in DB) ID uint `gorm:"primary_key",json:"id"` // Time the model object is created @@ -19,7 +19,7 @@ type commonModelFields struct { // User data model type User struct { - commonModelFields + CommonModelFields // Username of user Username string `gorm:"unique;not null",json:"username"` // Password of user @@ -34,7 +34,7 @@ type User struct { // Scenario data model type Scenario struct { - commonModelFields + CommonModelFields // Name of scenario Name string `gorm:"not null",json:"name"` // Running state of scenario @@ -51,7 +51,7 @@ type Scenario struct { // SimulationModel data model type SimulationModel struct { - commonModelFields + CommonModelFields // Name of simulation model Name string `gorm:"not null",json:"name"` // Number of output signals @@ -74,7 +74,7 @@ type SimulationModel struct { // Signal data model type Signal struct { - commonModelFields + CommonModelFields // Name of Signal Name string `json:"name"` // Unit of Signal @@ -89,7 +89,7 @@ type Signal struct { // Simulator data model type Simulator struct { - commonModelFields + CommonModelFields // UUID of the simulator UUID string `gorm:"not null",json:"uuid"` // Host if the simulator @@ -112,7 +112,7 @@ type Simulator struct { // Dashboard data model type Dashboard struct { - commonModelFields + CommonModelFields // Name of dashboard Name string `gorm:"not null",json:"name"` // Grid of dashboard @@ -125,7 +125,7 @@ type Dashboard struct { // Widget data model type Widget struct { - commonModelFields + CommonModelFields // Name of widget Name string `gorm:"not null",json:"name"` // Type of widget @@ -156,7 +156,7 @@ type Widget struct { // File data model type File struct { - commonModelFields + CommonModelFields // Name of file Name string `gorm:"not null",json:"name"` // Type of file (MIME type) From f84e6a1ddae454852fd76d0b6ce087c41cacac1c Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 31 Jul 2019 14:03:43 +0200 Subject: [PATCH 4/5] Fix in getUser endpoint for role User --- common/roles.go | 7 ++++++- routes/user/user_endpoints.go | 14 +++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/common/roles.go b/common/roles.go index 120e344..e5ce273 100644 --- a/common/roles.go +++ b/common/roles.go @@ -15,6 +15,7 @@ import ( type ModelName string const ModelUser = ModelName("user") +const ModelUsers = ModelName("users") const ModelScenario = ModelName("scenario") const ModelSimulator = ModelName("simulator") const ModelSimulatorAction = ModelName("simulatoraction") @@ -45,12 +46,14 @@ var crud = Permission{Create: true, Read: true, Update: true, Delete: true} var _ru_ = Permission{Create: false, Read: true, Update: true, Delete: false} var __u_ = Permission{Create: false, Read: false, Update: true, Delete: false} var _r__ = Permission{Create: false, Read: true, Update: false, Delete: false} +var none = Permission{Create: false, Read: false, Update: false, Delete: false} // Roles is used as a look up variable to determine if a certain user is // allowed to do a certain action on a given model based on his role var Roles = RoleActions{ "Admin": { ModelUser: crud, + ModelUsers: crud, ModelScenario: crud, ModelSimulationModel: crud, ModelSimulator: crud, @@ -61,7 +64,8 @@ var Roles = RoleActions{ ModelFile: crud, }, "User": { - ModelUser: __u_, + ModelUser: _ru_, + ModelUsers: none, ModelScenario: crud, ModelSimulationModel: crud, ModelSimulator: _r__, @@ -79,6 +83,7 @@ var Roles = RoleActions{ ModelSimulator: _r__, ModelSimulatorAction: _r__, ModelUser: _ru_, + ModelUsers: none, ModelSignal: _r__, ModelFile: _r__, }, diff --git a/routes/user/user_endpoints.go b/routes/user/user_endpoints.go index 564830b..ef86775 100644 --- a/routes/user/user_endpoints.go +++ b/routes/user/user_endpoints.go @@ -147,7 +147,7 @@ func authenticate(c *gin.Context) { // @Router /users [get] func getUsers(c *gin.Context) { - err := common.ValidateRole(c, common.ModelUser, common.Read) + err := common.ValidateRole(c, common.ModelUsers, common.Read) if err != nil { c.JSON(http.StatusUnprocessableEntity, fmt.Sprintf("%v", err)) return @@ -374,6 +374,17 @@ func getUser(c *gin.Context) { var user User id, _ := common.UintParamFromCtx(c, "userID") + reqUserID, _ := c.Get(common.UserIDCtx) + reqUserRole, _ := c.Get(common.UserRoleCtx) + + if id != reqUserID && reqUserRole != "Admin" { + c.JSON(http.StatusForbidden, gin.H{ + "success": false, + "message": "Invalid authorization", + }) + return + } + err = user.ByID(id) if err != nil { c.JSON(http.StatusNotFound, fmt.Sprintf("%v", err)) @@ -384,6 +395,7 @@ func getUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "user": serializer.Response(false), }) + } // DeleteUser godoc From 0b542f1b2eb35286b11a4247790d4ab64b15ef52 Mon Sep 17 00:00:00 2001 From: smavros Date: Wed, 28 Aug 2019 13:27:24 +0200 Subject: [PATCH 5/5] Added the bash curl script for API testing --- curl_villasAPI.sh | 109 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 curl_villasAPI.sh diff --git a/curl_villasAPI.sh b/curl_villasAPI.sh new file mode 100755 index 0000000..a58c1bf --- /dev/null +++ b/curl_villasAPI.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# Author: Stefanos Mavros 2019 +# +# Handy bash script using curl for testing the checkpoint of the +# VILLASweb frontend. See at the end of the file for usage cases + +port='4000' +version='v2' +apiBase='localhost:'"$port"'/api/'"$version" + +login () { + printf "> POST "$apiBase"/authenticate\n" + curl "$apiBase"/authenticate -s \ + -H "Contet-Type: application/json" \ + -X POST \ + --data "$1" | jq -r '.token' > auth.jwt \ + && printf '\n' +} + +create_user () { + printf "> POST "$apiBase"/users to create newUser\n" + curl "$apiBase"/users -s \ + -H "Contet-Type: application/json" \ + -H "Authorization: Bearer $(< auth.jwt)" \ + -X POST \ + --data "$1" | jq -r '.' && printf '\n' +} + +read_users () { + printf "> GET "$apiBase"/users\n" + curl "$apiBase"/users -s \ + -H "Contet-Type: application/json" \ + -H "Authorization: Bearer $(< auth.jwt)" \ + -X GET | jq '.' && printf '\n' +} + +read_simulators () { + printf "> GET "$apiBase"/simulators\n" + curl "$apiBase"/simulators -s \ + -H "Contet-Type: application/json" \ + -H "Authorization: Bearer $(< auth.jwt)" \ + -X GET | jq '.' && printf '\n' +} + +read_user () { + printf "> GET "$apiBase"/users/$1\n" + curl "$apiBase"/users/$1 -s \ + -H "Contet-Type: application/json" \ + -H "Authorization: Bearer $(< auth.jwt)" \ + -X GET | jq '.' && printf '\n' +} + +update_user () { + printf "> PUT "$apiBase"/users to update newUser\n" + curl "$apiBase"/users/$1 -s \ + -H "Contet-Type: application/json" \ + -H "Authorization: Bearer $(< auth.jwt)" \ + -X PUT \ + --data "$2" | jq -r '.' && printf '\n' +} + +delete_user () { + printf "> DELETE "$apiBase"/users/$1\n" + curl "$apiBase"/users/$1 -s \ + -H "Contet-Type: application/json" \ + -H "Authorization: Bearer $(< auth.jwt)" \ + -X DELETE | jq '.' && printf '\n' +} + + +admin='{ "Username" : "User_0", + "Password" : "xyz789", + "Role" : "Admin" }' +userA='{ "Username" : "User_A", + "Password" : "abc123", + "Mail" : "m@i.l" }' +newUserW='{ "user": { "Username" : "User_W", + "Password" : "www747_tst", + "Role" : "User", + "Mail" : "m@i.l" } }' +updUserW='{ "user": { "Mail" : "lalala", + "Role" : "Admin" } }' +#updUserW='{ "user": { "Username" : "User_Wupdated", + #"Password" : "pie314_test", + #"Role" : "Admin", + #"Mail" : "NEW_m@i.l" } }' +userC='{ "user": { "Username" : "User_C", + "Password" : "abc123", + "Mail" : "C_m@i.l", + "Role" : "User"} }' + +login "$admin" +#create_user "$userC" +#read_users +#read_user 1 +#read_simulators +create_user "$newUserW" +#read_users +read_user 4 +#login "$newUserW" +update_user 4 "$updUserW" +#login "$admin" +read_user 4 +#login "$admin" +#read_user 4 +#login "$updUserW" +#delete_user 2 +