Compare commits

..

2 Commits

Author SHA1 Message Date
0f1c8e327e Mod permission middleware to only request database once
All checks were successful
Build Backend (NixCN CMS) TeamCity build finished
Build Frontend (NixCN CMS) TeamCity build finished
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-06 10:40:48 +08:00
ddffb0da23 Add permission middleware
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-06 10:36:51 +08:00
7 changed files with 64 additions and 46 deletions

View File

@@ -14,14 +14,12 @@ func JWTAuth(required bool) gin.HandlerFunc {
authtoken := new(authtoken.Token)
uid, err := authtoken.HeaderVerify(auth)
if err != nil {
c.JSON(401, gin.H{"status": err.Error()})
c.Abort()
c.AbortWithStatusJSON(401, gin.H{"status": err.Error()})
return
}
if required == true && uid == "" {
c.JSON(401, gin.H{"status": "unauthorized"})
c.Abort()
c.AbortWithStatusJSON(401, gin.H{"status": "unauthorized"})
return
}

46
middleware/permission.go Normal file
View File

@@ -0,0 +1,46 @@
package middleware
import (
"nixcn-cms/data"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func Permission(requiredLevel uint) gin.HandlerFunc {
return func(c *gin.Context) {
var permissionLevel uint
permissionLevelPrev, ok := c.Get("permission_level")
if !ok {
userIdOrig, ok := c.Get("user_id")
if !ok || userIdOrig.(string) == "" {
c.AbortWithStatusJSON(401, gin.H{"status": "missing user id"})
return
}
userId, err := uuid.Parse(userIdOrig.(string))
if err != nil {
c.AbortWithStatusJSON(500, gin.H{"status": "error parsing user id"})
return
}
userData, err := new(data.User).GetByUserId(userId)
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"status": "user not found"})
return
}
permissionLevel = userData.PermissionLevel
c.Set("permission_level", userData.PermissionLevel)
} else {
permissionLevel = permissionLevelPrev.(uint)
}
if permissionLevel < requiredLevel {
c.AbortWithStatusJSON(403, gin.H{"status": "permission denied"})
return
}
c.Next()
}
}

View File

@@ -43,29 +43,13 @@ func Checkin(c *gin.Context) {
}
func CheckinSubmit(c *gin.Context) {
userIdOrig, ok := c.Get("user_id")
if userIdOrig.(string) == "" || !ok {
c.JSON(401, gin.H{"status": "unauthorized"})
}
userId, err := uuid.Parse(userIdOrig.(string))
if err != nil {
c.JSON(500, gin.H{"status": "failed to parse uuid"})
}
userData := new(data.User)
userData.GetByUserId(userId)
if userData.PermissionLevel <= 20 {
c.JSON(403, gin.H{"status": "access denied"})
return
}
var req struct {
ChekinCode string `json:"checkin_code"`
}
c.ShouldBindJSON(&req)
attendanceData := new(data.Attendance)
err = attendanceData.VerifyCheckinCode(req.ChekinCode)
err := attendanceData.VerifyCheckinCode(req.ChekinCode)
if err != nil {
c.JSON(400, gin.H{"status": "error verify checkin code"})
return

View File

@@ -19,18 +19,12 @@ func Full(c *gin.Context) {
return
}
userData := new(data.User)
user, err := userData.GetByUserId(userId)
userData, err := new(data.User).GetByUserId(userId)
if err != nil {
c.JSON(404, gin.H{"status": "user not found"})
return
}
if user.PermissionLevel < 50 {
c.JSON(403, gin.H{"status": "permission denied"})
return
}
data, err := userData.GetFullTable()
if err != nil {
c.JSON(500, gin.H{"status": "database error"})

View File

@@ -7,12 +7,12 @@ import (
)
func Handler(r *gin.RouterGroup) {
r.Use(middleware.JWTAuth(true))
r.Use(middleware.JWTAuth(true), middleware.Permission(10))
r.GET("/info", Info)
r.GET("/checkin", Checkin)
r.POST("/checkin/submit", CheckinSubmit)
r.POST("/checkin/submit", CheckinSubmit, middleware.Permission(20))
r.PATCH("/update", Update)
r.GET("/list", List)
r.GET("/query", Query)
r.POST("/full", Full)
r.POST("/full", Full, middleware.Permission(50))
}

View File

@@ -8,8 +8,6 @@ import (
)
func List(c *gin.Context) {
data := new(data.User)
// Get limit and offset from query
limit, ok := c.GetQuery("limit")
if !ok {
@@ -34,7 +32,7 @@ func List(c *gin.Context) {
}
// Get user list from search engine
list, err := data.FastListUsers(limitNum, offsetNum)
list, err := new(data.User).FastListUsers(limitNum, offsetNum)
if err != nil {
c.JSON(500, gin.H{"status": "failed list users from meilisearch"})
}

View File

@@ -10,7 +10,6 @@ import (
func Update(c *gin.Context) {
// New user model
user := new(data.User)
userIdOrig, ok := c.Get("user_id")
if !ok {
c.JSON(403, gin.H{"status": "userid error"})
@@ -19,34 +18,33 @@ func Update(c *gin.Context) {
userId, err := uuid.Parse(userIdOrig.(string))
if err != nil {
c.JSON(500, gin.H{"status": "failed to parse uuid"})
return
}
var ReqInfo data.User
c.BindJSON(&ReqInfo)
// Get user info
user.GetByUserId(userId)
// Reject permission 0 user
if user.PermissionLevel == 0 {
c.JSON(403, gin.H{"status": "premission denied"})
userData, err := new(data.User).GetByUserId(userId)
if err != nil {
c.JSON(500, gin.H{"status": "failed to find user"})
return
}
user.Avatar = ReqInfo.Avatar
user.Email = ReqInfo.Email
user.Nickname = ReqInfo.Nickname
user.Subtitle = ReqInfo.Subtitle
userData.Avatar = ReqInfo.Avatar
userData.Email = ReqInfo.Email
userData.Nickname = ReqInfo.Nickname
userData.Subtitle = ReqInfo.Subtitle
if ReqInfo.Bio != "" {
if !cryptography.IsBase64Std(ReqInfo.Bio) {
c.JSON(400, gin.H{"status": "invalid base64"})
}
}
user.Bio = ReqInfo.Bio
userData.Bio = ReqInfo.Bio
// Update user info
user.UpdateByUserID(userId)
userData.UpdateByUserID(userId)
c.JSON(200, gin.H{"status": "success"})
}