diff --git a/controllers/roomController.go b/controllers/roomController.go index 20e7375..c5d0dcc 100644 --- a/controllers/roomController.go +++ b/controllers/roomController.go @@ -21,6 +21,8 @@ type CRUDController interface { type RoomController interface { CRUDController + GetUsers(*gin.Context) + AddUser(*gin.Context) } type roomController struct { @@ -65,7 +67,14 @@ func (rc *roomController) GetById(c *gin.Context) { } func (rc *roomController) Create(c *gin.Context) { + user, exists := c.Get("user") + if !exists { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + room, err := models.NewRoom(c) + room.Admins = append(room.Admins, user.(models.User)) if err != nil { ReplyError(c, err) @@ -77,6 +86,14 @@ func (rc *roomController) Create(c *gin.Context) { return } + //userID := user.(models.User).ID + //rc.DB.Model(&models.Room{}).Where("id = ?"), room.ID).Association("Admins").Append(&models.User{ID: userID}) + + //if result.Error != nil { + // ReplyError(c, fmt.Errorf("Room creation failed: %s", result.Error)) + // return + //} + ReplyOK(c, "Room was created") } @@ -124,6 +141,83 @@ func (rc *roomController) Delete(c *gin.Context) { ReplyOK(c, "Room was deleted") } +func (rc *roomController) GetUsers(c *gin.Context) { + //only allow room admin + + roomId, err := strconv.Atoi(c.Param("id")) + + if err != nil { + ReplyError(c, fmt.Errorf("Room with Id '%s' does not exist", c.Param("id"))) + return + } + + var room models.Room + result := rc.DB.First(&room, uint(roomId)) + + if result.Error != nil { + ReplyError(c, fmt.Errorf("Could not query room: %v", result.Error)) + return + } + + var users []models.User + rc.DB.Model(&room).Association("Users").Find(&users) + + var emails []string + for _, user := range users { + emails = append(emails, user.Email) + } + + ReplyOK(c, emails) +} + +func (rc *roomController) AddUser(c *gin.Context) { + //only allow room admin + + var body struct { + Email string + } + + err := c.Bind(&body) + + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Failed to read body", + }) + + return + } + + //lookup requested user + var user models.User + result := rc.DB.First(&user, "email = ?", body.Email) + + if result.Error != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Invalid user", + }) + + return + } + + roomId, err := strconv.Atoi(c.Param("id")) + + if err != nil { + ReplyError(c, fmt.Errorf("Room with Id '%s' does not exist", c.Param("id"))) + return + } + + var room models.Room + result = rc.DB.First(&room, uint(roomId)) + + if result.Error != nil { + ReplyError(c, fmt.Errorf("Could not query room: %v", result.Error)) + return + } + + rc.DB.Model(&room).Association("Users").Append(&user) + ReplyOK(c, "Added User to Room") +} + func ReplyError(ctx *gin.Context, err error) { ctx.JSON(http.StatusBadRequest, gin.H{ "error": err.Error() }) } diff --git a/controllers/userController.go b/controllers/userController.go index 7c76033..66e2a46 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -2,6 +2,7 @@ package controllers import( "os" + "fmt" "time" "net/http" "golang.org/x/crypto/bcrypt" @@ -25,9 +26,10 @@ func NewUserController(db *gorm.DB) UserController { } -func (uc *UserController) Signup(c *gin.Context) { +func (uc *UserController) Register(c *gin.Context) { //Get the email/passwd off req body var body struct { + Name string Email string Password string } @@ -54,10 +56,11 @@ func (uc *UserController) Signup(c *gin.Context) { } //create user - user := models.User{Email: body.Email, Password: string(hash)} + user := models.User{Name: body.Name, Email: body.Email, Password: string(hash)} result := uc.DB.Create(&user) if result.Error != nil { + fmt.Println("Error: ", result.Error) c.JSON(http.StatusBadRequest, gin.H{ "error": "Failed to create user", }) @@ -136,9 +139,15 @@ func (uc *UserController) Login(c *gin.Context) { } func (uc *UserController) Validate(c *gin.Context) { - user, _ := c.Get("user") + user, exists := c.Get("user") - c.JSON(http.StatusOK, gin.H{ - "message": user, - }) + if exists { + c.JSON(http.StatusOK, gin.H{ + "message": fmt.Sprintf("Logged in with userID: %d", user.(models.User).ID), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "message": "Currently not logged in.", + }) + } } diff --git a/main.go b/main.go index d25159c..4d16222 100644 --- a/main.go +++ b/main.go @@ -70,15 +70,18 @@ func main() { apiRoutes := server.Group("/api") //apiRoutes.Use(middlewares.BasicAuth()) { - apiRoutes.POST("/rooms", roomController.Create) - apiRoutes.GET("/rooms", roomController.GetAll) - apiRoutes.GET("/rooms/:id", roomController.GetById) - apiRoutes.PUT("/rooms/:id", roomController.Update) - apiRoutes.DELETE("/rooms/:id", roomController.Delete) + apiRoutes.POST("/rooms", authValidator.RequireAuth, roomController.Create) + apiRoutes.GET("/rooms", authValidator.OptionalAuth, roomController.GetAll) + apiRoutes.GET("/rooms/:id", authValidator.OptionalAuth, roomController.GetById) + apiRoutes.PUT("/rooms/:id", authValidator.RequireAuth, roomController.Update) + apiRoutes.DELETE("/rooms/:id", authValidator.RequireAuth, roomController.Delete) - apiRoutes.POST("/users/signup", userController.Signup) + apiRoutes.GET("/rooms/:id/users", authValidator.RequireAuth, authValidator.RequireRoomAdmin, roomController.GetUsers) + apiRoutes.POST("/rooms/:id/users", authValidator.RequireAuth, roomController.AddUser) + + apiRoutes.POST("/users/register", userController.Register) apiRoutes.POST("/users/login", userController.Login) - apiRoutes.GET("/users/validate", authValidator.RequireAuth, userController.Validate) + apiRoutes.GET("/users/validate", authValidator.OptionalAuth, userController.Validate) } server.Run(":"+os.Getenv("PORT")) diff --git a/middlewares/requireAuth.go b/middlewares/requireAuth.go index 442bc04..02899b2 100644 --- a/middlewares/requireAuth.go +++ b/middlewares/requireAuth.go @@ -4,6 +4,7 @@ import( "os" "fmt" "time" + "strconv" "net/http" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" @@ -16,6 +17,32 @@ type AuthValidator struct { DB *gorm.DB } +func (av *AuthValidator) RequireRoomAdmin(c *gin.Context) { + user, exists := c.Get("user") + if !exists { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + roomId, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ "message": fmt.Sprintf("Room with Id '%s' does not exist", c.Param("id"))}) + return + } + + var rooms []models.Room + av.DB.Model(&user).Association("OwnedRooms").Find(&rooms) + + for _, room := range rooms { + if room.ID == uint(roomId) { + c.Next() + return + } + } + + c.AbortWithStatus(http.StatusUnauthorized) +} + func (av *AuthValidator) RequireAuth(c *gin.Context) { // Get Cookie tokenString, err := c.Cookie("Authorization") @@ -68,3 +95,43 @@ func (av *AuthValidator) RequireAuth(c *gin.Context) { c.AbortWithStatus(http.StatusUnauthorized) } + +func (av *AuthValidator) OptionalAuth(c *gin.Context) { + defer c.Next() + + // Get Cookie + tokenString, err := c.Cookie("Authorization") + + if err != nil { + return + } + + //Validate + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + return []byte(os.Getenv("SECRET")), nil + }) + + if err != nil { + return + } + + if claims, ok := token.Claims.(jwt.MapClaims); ok { + if float64(time.Now().Unix()) > claims["exp"].(float64) { + return + } + + //Find user + var user models.User + result := av.DB.First(&user, claims["sub"]) + + if result.Error != nil { + return + } + + //Attach to req + c.Set("user", user) + } +} diff --git a/models/room.go b/models/room.go index 942d0a4..9a79f99 100644 --- a/models/room.go +++ b/models/room.go @@ -11,8 +11,9 @@ type Room struct { Address string `json:"address"` Website string `json:"website"` Capacity int `json:"capacity"` - IsPublic bool `json:"isPublic" gorm:"default:false"` + IsPublic bool `json:"isPublic" gorm:"default:true"` Admins []User `gorm:"many2many:room_admins;"` + Users []User `gorm:"many2many:room_users;"` } func NewRoom(ctx *gin.Context) (Room, error) { diff --git a/models/user.go b/models/user.go index 4b16094..9f5f42d 100644 --- a/models/user.go +++ b/models/user.go @@ -9,4 +9,5 @@ type User struct { Name string `json:"name" binding:"required" gorm:"unique;not null"` Password string `json:"password" binding:"required" gorm:"not null"` Email string `json:"email" binding:"required,email" gorm:"unique;not null"` + OwnedRooms []Room `gorm:"many2many:room_admins;"` }