From cc4a890064f63c578452998b3c2a28996da44b14 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Wed, 29 Mar 2023 19:52:46 +1100 Subject: [PATCH] admin only route is working --- controllers/retrieve_secrets.go | 20 +++++++++++++++ controllers/store_secrets.go | 24 +++++++++++++++++ main.go | 14 +++++----- middlewares/middlewares.go | 44 +++++++++++++++++++++++++++++++- models/{secrets.go => secret.go} | 0 models/setup.go | 10 +++++++- models/user.go | 25 +++++++++++++++++- 7 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 controllers/retrieve_secrets.go create mode 100644 controllers/store_secrets.go rename models/{secrets.go => secret.go} (100%) diff --git a/controllers/retrieve_secrets.go b/controllers/retrieve_secrets.go new file mode 100644 index 0000000..b9df7a2 --- /dev/null +++ b/controllers/retrieve_secrets.go @@ -0,0 +1,20 @@ +package controllers + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type RetrieveInput struct { + DeviceName string `json:"deviceName" binding:"required"` +} + +func Retrieve(c *gin.Context) { + var input RetrieveInput + + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } +} diff --git a/controllers/store_secrets.go b/controllers/store_secrets.go new file mode 100644 index 0000000..35ce3bd --- /dev/null +++ b/controllers/store_secrets.go @@ -0,0 +1,24 @@ +package controllers + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +// bindings are validated by https://github.com/go-playground/validator +type StoreInput struct { + RoleId int `json:"roleId"` + DeviceName string `json:"deviceName" binding:"required"` + UserName string `json:"userName" binding:"required"` + SecretValue string `json:"secretValue" binding:"required"` +} + +func Store(c *gin.Context) { + var input RetrieveInput + + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } +} diff --git a/main.go b/main.go index 4ea46f9..877c684 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ func main() { router := gin.Default() router.GET("/", func(c *gin.Context) { - time.Sleep(10 * time.Second) + //time.Sleep(10 * time.Second) c.String(http.StatusOK, "Welcome Gin Server") }) @@ -36,18 +36,18 @@ func main() { // Register our routes public := router.Group("/api") - public.POST("/register", controllers.Register) public.POST("/login", controllers.Login) - // This is just PoC really, we can get rid of it - //protected := r.Group("/api/admin") - //protected.Use(middlewares.JwtAuthMiddleware()) - //protected.GET("/user", controllers.CurrentUser) + // TODO - this should be an authenticated route + adminOnly := router.Group("/api/admin") + adminOnly.Use(middlewares.JwtAuthAdminMiddleware()) + adminOnly.POST("/register", controllers.Register) // Get secrets protected := router.Group("/api/secret") protected.Use(middlewares.JwtAuthMiddleware()) - protected.GET("/device", controllers.CurrentUser) + protected.GET("/retrieve", controllers.Retrieve) + protected.POST("/store", controllers.Store) // Initializing the server in a goroutine so that // it won't block the graceful shutdown handling below diff --git a/middlewares/middlewares.go b/middlewares/middlewares.go index 0523fc5..1f9b126 100644 --- a/middlewares/middlewares.go +++ b/middlewares/middlewares.go @@ -1,8 +1,10 @@ package middlewares import ( + "fmt" "net/http" + "ccsecrets/models" "ccsecrets/utils/token" "github.com/gin-gonic/gin" @@ -18,5 +20,45 @@ func JwtAuthMiddleware() gin.HandlerFunc { } c.Next() } - +} + +func JwtAuthAdminMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + + // TODO - also verify user role of admin + + err := token.TokenValid(c) + if err != nil { + c.String(http.StatusUnauthorized, "Unauthorized") + c.Abort() + return + } + + // Once we know the token is valid, figure out if this user is an admin + user_id, err := token.ExtractTokenID(c) + + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.Abort() + return + } + + ur, err := models.GetUserRoleByID(user_id) + + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.Abort() + return + } + fmt.Printf("JwtAuthAdminMiddleware retrieved UserRole object '%v'\n", ur) + + if !ur.Admin { + c.String(http.StatusUnauthorized, "User role is Non-Admin") + c.Abort() + return + } + + // What does this do? + c.Next() + } } diff --git a/models/secrets.go b/models/secret.go similarity index 100% rename from models/secrets.go rename to models/secret.go diff --git a/models/setup.go b/models/setup.go index 4737f48..7639daa 100644 --- a/models/setup.go +++ b/models/setup.go @@ -107,6 +107,14 @@ func CreateTables() { fmt.Printf("Error adding initial admin role : '%s'", err) os.Exit(1) } + if _, err = db.Exec("INSERT INTO roles VALUES(2, 'UserRole', false, false);"); err != nil { + fmt.Printf("Error adding initial admin role : '%s'", err) + os.Exit(1) + } + if _, err = db.Exec("INSERT INTO roles VALUES(3, 'GuestRole', true, false);"); err != nil { + fmt.Printf("Error adding initial admin role : '%s'", err) + os.Exit(1) + } } // Users table @@ -116,7 +124,7 @@ func CreateTables() { } rowCount, _ = CheckCount("users") if rowCount == 0 { - if _, err = db.Exec("INSERT INTO users VALUES(1, 1, 'Administrator', 'password', 'token');"); err != nil { + if _, err = db.Exec("INSERT INTO users VALUES(1, 1, 'Administrator', '$2a$10$k1qldm.bWqZsQWrKPdahR.Pfz5LxkMUka2.8INEeSD7euzkiznIR.', 'token');"); err != nil { fmt.Printf("Error adding initial admin role : '%s'", err) os.Exit(1) } diff --git a/models/user.go b/models/user.go index fbdae6f..4211747 100644 --- a/models/user.go +++ b/models/user.go @@ -16,6 +16,13 @@ type User struct { AccessToken string `db:"AccessToken"` } +type UserRole struct { + User + RoleName string `db:"RoleName"` + ReadOnly bool `db:"ReadOnly"` + Admin bool `db:"Admin"` +} + func (u *User) SaveUser() (*User, error) { var err error @@ -81,7 +88,7 @@ func GetUserByID(uid uint) (User, error) { var u User // Query database for matching user object - err := db.QueryRowx("SELECT * FROM Users WHERE UserId=?", uid).StructScan(&u) + err := db.QueryRowx("SELECT * FROM users INNER JOIN roles ON users.RoleId = roles.RoleId WHERE UserId=?", uid).StructScan(&u) if err != nil { return u, errors.New("user not found") } @@ -96,6 +103,22 @@ func GetUserByID(uid uint) (User, error) { } +func GetUserRoleByID(uid uint) (UserRole, error) { + + var ur UserRole + + // Query database for matching user object + fmt.Printf("GetUserRoleByID querying for userid '%d'\n", uid) + err := db.QueryRowx("SELECT users.UserId, users.RoleId, users.UserName, users.Password, users.AccessToken, roles.RoleName, roles.ReadOnly, roles.Admin FROM users INNER JOIN roles ON users.RoleId = roles.RoleId WHERE users.UserId=?", uid).StructScan(&ur) + if err != nil { + fmt.Printf("GetUserRoleByID received error when querying database : '%s'\n", err) + return ur, errors.New("GetUserRoleByID user not found") + } + + return ur, nil + +} + func (u *User) PrepareGive() { u.Password = "" }