chenged display of devices according to user`s role. all changes are made in backend

This commit is contained in:
tdv
2025-09-04 19:09:14 +03:00
parent c38dd658f5
commit 615abf42d2
16 changed files with 419 additions and 7 deletions

View File

@@ -26,12 +26,52 @@ func (h *DevicesHandler) List(c *gin.Context) {
limit = 50
}
var total int64
h.db.Model(&models.Device{}).Count(&total)
// Get user context
userContext, exists := c.Get("user")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}
user, ok := userContext.(UserContext)
if !ok {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid user data"})
return
}
var total int64
var devs []models.Device
if err := h.db.Preload("Users").Offset(offset).Limit(limit).Find(&devs).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
var err error
if user.Role == models.RoleAdmin {
// Admin user - show all devices
err = h.db.Model(&models.Device{}).Count(&total).Error
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "count query failed: " + err.Error()})
return
}
err = h.db.Preload("Users").Offset(offset).Limit(limit).Find(&devs).Error
} else {
err = h.db.Model(&models.Device{}).
Joins("INNER JOIN user_devices ON user_devices.id = devices.guid").
Where("user_devices.guid = ?", user.ID).
Count(&total).Error
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "count query failed: " + err.Error()})
return
}
err = h.db.Preload("Users").
Joins("INNER JOIN user_devices ON user_devices.id = devices.guid").
Where("user_devices.guid = ?", user.ID).
Offset(offset).Limit(limit).
Find(&devs).Error
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed: " + err.Error()})
return
}
@@ -39,6 +79,7 @@ func (h *DevicesHandler) List(c *gin.Context) {
for _, d := range devs {
out = append(out, dto.MapDevice(d))
}
c.JSON(http.StatusOK, dto.DeviceListDto{Devices: out, Offset: offset, Limit: limit, Total: total})
}

View File

@@ -3,12 +3,20 @@ package handlers
import (
"net/http"
"smoop-api/internal/crypto"
"smoop-api/internal/models"
"strings"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
// UserContext holds structured user information from JWT
type UserContext struct {
ID uint `json:"id"`
Username string `json:"username"`
Role models.Role `json:"role"`
}
func Auth(jwtMgr *crypto.JWTManager) gin.HandlerFunc {
return func(c *gin.Context) {
h := c.GetHeader("Authorization")
@@ -23,6 +31,12 @@ func Auth(jwtMgr *crypto.JWTManager) gin.HandlerFunc {
return
}
claims, _ := token.Claims.(jwt.MapClaims)
userContext := UserContext{
ID: uint(claims["sub"].(float64)),
Username: claims["name"].(string),
Role: models.Role(claims["role"].(string)),
}
c.Set("user", userContext)
c.Set("claims", claims)
c.Next()
}
@@ -30,8 +44,16 @@ func Auth(jwtMgr *crypto.JWTManager) gin.HandlerFunc {
func RequireRole(role string) gin.HandlerFunc {
return func(c *gin.Context) {
claims := MustClaims(c)
if ClaimRole(claims) != role {
userContext, exists := c.Get("user")
if !exists {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
}
user, ok := userContext.(UserContext)
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid user data"})
return
}
if string(user.Role) != role {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "forbidden"})
return
}
@@ -76,3 +98,14 @@ func ClaimRole(claims map[string]interface{}) string {
}
return ""
}
// New helper to get UserContext from context
func GetUserContext(c *gin.Context) (UserContext, bool) {
userContext, exists := c.Get("user")
if !exists {
return UserContext{}, false
}
user, ok := userContext.(UserContext)
return user, ok
}

View File

@@ -0,0 +1,37 @@
package middleware
import (
"smoop-api/internal/handlers"
"smoop-api/internal/models"
"github.com/gin-gonic/gin"
)
// DeviceAccessFilter middleware sets filtering context for device access
func DeviceAccessFilter() gin.HandlerFunc {
return func(c *gin.Context) {
userContext, exists := c.Get("user")
if !exists {
c.JSON(401, gin.H{"error": "unauthorized"})
c.Abort()
return
}
user, ok := userContext.(handlers.UserContext)
if !ok {
c.JSON(401, gin.H{"error": "invalid user data"})
c.Abort()
return
}
// Set filter flag and user ID in context
if user.Role == models.RoleAdmin {
c.Set("filterDevices", false) // Admin sees all devices
} else {
c.Set("filterDevices", true) // Regular user needs filtering
c.Set("userID", user.ID) // Store user ID for filtering
}
c.Next()
}
}

View File

@@ -11,6 +11,7 @@ import (
"smoop-api/internal/config"
"smoop-api/internal/crypto"
"smoop-api/internal/handlers"
"smoop-api/internal/middleware"
)
func Build(db *gorm.DB, minio *minio.Client, cfg *config.Config) *gin.Engine {
@@ -41,7 +42,7 @@ func Build(db *gorm.DB, minio *minio.Client, cfg *config.Config) *gin.Engine {
r.GET("/users", authMW, adminOnly, usersH.List)
r.POST("/users/create", authMW, adminOnly, usersH.Create)
r.GET("/devices", authMW, devH.List)
r.GET("/devices", authMW, middleware.DeviceAccessFilter(), devH.List)
r.POST("/devices/create", authMW, devH.Create)
r.POST("/devices/:guid/rename", authMW, devH.Rename)
r.POST("/devices/:guid/add_to_user", authMW, devH.AddToUser)