feat(middleware): add AdminRequired authorization middleware

AdminRequired gates admin endpoints by checking user IsAdmin flag.

Placed AFTER AuthRequired; fetches user from repository, returns 403 for non-admins.

Soft-deleted users are rejected with 401 since FindByID excludes them.

Tests: admin passes, non-admin forbidden, soft-deleted admin rejected, missing user ID.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-07-04 17:20:30 +08:00
parent 032f716e49
commit ac98b5ddbd
2 changed files with 196 additions and 0 deletions
+31
View File
@@ -8,6 +8,7 @@ import (
"github.com/dhao2001/mygo/internal/api"
"github.com/dhao2001/mygo/internal/auth"
"github.com/dhao2001/mygo/internal/repository"
)
const userIDKey = "user_id"
@@ -68,3 +69,33 @@ func MustGetUserID(c *gin.Context) string {
}
return userID
}
// AdminRequired returns a Gin middleware that gates access to admin-only
// endpoints. It must be applied AFTER AuthRequired. It fetches the user from
// the repository, and returns 403 if the user is not an admin. Soft-deleted
// users are not found by FindByID and will receive a 401 response.
func AdminRequired(userRepo repository.UserRepository) gin.HandlerFunc {
return func(c *gin.Context) {
userID := GetUserID(c)
if userID == "" {
api.Error(c, http.StatusUnauthorized, "missing user context")
c.Abort()
return
}
user, err := userRepo.FindByID(c.Request.Context(), userID)
if err != nil {
api.Error(c, http.StatusUnauthorized, "user not found")
c.Abort()
return
}
if !user.IsAdmin {
api.Error(c, http.StatusForbidden, "admin access required")
c.Abort()
return
}
c.Next()
}
}