Allow adding users to rooms

This commit is contained in:
2025-01-04 23:12:56 +01:00
parent c78a84e075
commit 2eadad9135
6 changed files with 189 additions and 14 deletions

View File

@@ -21,6 +21,8 @@ type CRUDController interface {
type RoomController interface { type RoomController interface {
CRUDController CRUDController
GetUsers(*gin.Context)
AddUser(*gin.Context)
} }
type roomController struct { type roomController struct {
@@ -65,7 +67,14 @@ func (rc *roomController) GetById(c *gin.Context) {
} }
func (rc *roomController) Create(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, err := models.NewRoom(c)
room.Admins = append(room.Admins, user.(models.User))
if err != nil { if err != nil {
ReplyError(c, err) ReplyError(c, err)
@@ -77,6 +86,14 @@ func (rc *roomController) Create(c *gin.Context) {
return 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") ReplyOK(c, "Room was created")
} }
@@ -124,6 +141,83 @@ func (rc *roomController) Delete(c *gin.Context) {
ReplyOK(c, "Room was deleted") 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) { func ReplyError(ctx *gin.Context, err error) {
ctx.JSON(http.StatusBadRequest, gin.H{ "error": err.Error() }) ctx.JSON(http.StatusBadRequest, gin.H{ "error": err.Error() })
} }

View File

@@ -2,6 +2,7 @@ package controllers
import( import(
"os" "os"
"fmt"
"time" "time"
"net/http" "net/http"
"golang.org/x/crypto/bcrypt" "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 //Get the email/passwd off req body
var body struct { var body struct {
Name string
Email string Email string
Password string Password string
} }
@@ -54,10 +56,11 @@ func (uc *UserController) Signup(c *gin.Context) {
} }
//create user //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) result := uc.DB.Create(&user)
if result.Error != nil { if result.Error != nil {
fmt.Println("Error: ", result.Error)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": "Failed to create user", "error": "Failed to create user",
}) })
@@ -136,9 +139,15 @@ func (uc *UserController) Login(c *gin.Context) {
} }
func (uc *UserController) Validate(c *gin.Context) { func (uc *UserController) Validate(c *gin.Context) {
user, _ := c.Get("user") user, exists := c.Get("user")
if exists {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": user, "message": fmt.Sprintf("Logged in with userID: %d", user.(models.User).ID),
}) })
} else {
c.JSON(http.StatusOK, gin.H{
"message": "Currently not logged in.",
})
}
} }

17
main.go
View File

@@ -70,15 +70,18 @@ func main() {
apiRoutes := server.Group("/api") apiRoutes := server.Group("/api")
//apiRoutes.Use(middlewares.BasicAuth()) //apiRoutes.Use(middlewares.BasicAuth())
{ {
apiRoutes.POST("/rooms", roomController.Create) apiRoutes.POST("/rooms", authValidator.RequireAuth, roomController.Create)
apiRoutes.GET("/rooms", roomController.GetAll) apiRoutes.GET("/rooms", authValidator.OptionalAuth, roomController.GetAll)
apiRoutes.GET("/rooms/:id", roomController.GetById) apiRoutes.GET("/rooms/:id", authValidator.OptionalAuth, roomController.GetById)
apiRoutes.PUT("/rooms/:id", roomController.Update) apiRoutes.PUT("/rooms/:id", authValidator.RequireAuth, roomController.Update)
apiRoutes.DELETE("/rooms/:id", roomController.Delete) 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.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")) server.Run(":"+os.Getenv("PORT"))

View File

@@ -4,6 +4,7 @@ import(
"os" "os"
"fmt" "fmt"
"time" "time"
"strconv"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@@ -16,6 +17,32 @@ type AuthValidator struct {
DB *gorm.DB 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) { func (av *AuthValidator) RequireAuth(c *gin.Context) {
// Get Cookie // Get Cookie
tokenString, err := c.Cookie("Authorization") tokenString, err := c.Cookie("Authorization")
@@ -68,3 +95,43 @@ func (av *AuthValidator) RequireAuth(c *gin.Context) {
c.AbortWithStatus(http.StatusUnauthorized) 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)
}
}

View File

@@ -11,8 +11,9 @@ type Room struct {
Address string `json:"address"` Address string `json:"address"`
Website string `json:"website"` Website string `json:"website"`
Capacity int `json:"capacity"` Capacity int `json:"capacity"`
IsPublic bool `json:"isPublic" gorm:"default:false"` IsPublic bool `json:"isPublic" gorm:"default:true"`
Admins []User `gorm:"many2many:room_admins;"` Admins []User `gorm:"many2many:room_admins;"`
Users []User `gorm:"many2many:room_users;"`
} }
func NewRoom(ctx *gin.Context) (Room, error) { func NewRoom(ctx *gin.Context) (Room, error) {

View File

@@ -9,4 +9,5 @@ type User struct {
Name string `json:"name" binding:"required" gorm:"unique;not null"` Name string `json:"name" binding:"required" gorm:"unique;not null"`
Password string `json:"password" binding:"required" gorm:"not null"` Password string `json:"password" binding:"required" gorm:"not null"`
Email string `json:"email" binding:"required,email" gorm:"unique;not null"` Email string `json:"email" binding:"required,email" gorm:"unique;not null"`
OwnedRooms []Room `gorm:"many2many:room_admins;"`
} }