From 4761a1791233127c7ffe51854ea729064d2d5ab3 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 13 Jan 2026 17:18:12 +0100 Subject: [PATCH 1/4] feat: check if user has been validated to access area --- middleware/admin.go | 7 +++++++ middleware/authentification.go | 4 ++-- router/router.go | 4 ++++ schema.prisma | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 middleware/admin.go diff --git a/middleware/admin.go b/middleware/admin.go new file mode 100644 index 0000000..34deae7 --- /dev/null +++ b/middleware/admin.go @@ -0,0 +1,7 @@ +package middleware + +import "github.com/gin-gonic/gin" + +func CheckAdminAccess(ctx *gin.Context) { + +} diff --git a/middleware/authentification.go b/middleware/authentification.go index 4689cde..e948fa9 100644 --- a/middleware/authentification.go +++ b/middleware/authentification.go @@ -69,8 +69,8 @@ func CheckUserAccess(ctx *gin.Context) { db.Users.ID.Equals(userID), ).Exec(ctx) - if err != nil || user == nil { - ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "user not found"}) + if err != nil || user == nil || user.Authorized != true { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "user not found or not authorized"}) return } diff --git a/router/router.go b/router/router.go index a567830..e87fa66 100644 --- a/router/router.go +++ b/router/router.go @@ -66,6 +66,10 @@ func setupProtectedRouter(router *gin.Engine) *gin.Engine { protectedRoute.POST("/areas/:areaId/action/add", controller.LinkAction) protectedRoute.POST("/areas/:areaId/reaction/add", controller.LinkReaction) } + protectedRoute.Use(middleware.CheckAdminAccess) + { + // protectedRoute.PATCH("/areas/:userId/status", controller.UpdateUserStatus) + } return router } diff --git a/schema.prisma b/schema.prisma index f4f472f..fc098df 100644 --- a/schema.prisma +++ b/schema.prisma @@ -15,6 +15,8 @@ model Users { email String @unique passwordHash String @map("password_hash") createdAt DateTime @default(now()) @map("created_at") + admin Boolean @default(false) + authorized Boolean @default(false) areas Areas[] serviceTokens UserServiceTokens[] From 08b62c9e09ab1540c194f84a7d7d6837ef23b833 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 13 Jan 2026 17:20:08 +0100 Subject: [PATCH 2/4] fix: user not allowed can login if admin --- middleware/authentification.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/authentification.go b/middleware/authentification.go index e948fa9..c558f36 100644 --- a/middleware/authentification.go +++ b/middleware/authentification.go @@ -69,7 +69,7 @@ func CheckUserAccess(ctx *gin.Context) { db.Users.ID.Equals(userID), ).Exec(ctx) - if err != nil || user == nil || user.Authorized != true { + if err != nil || user == nil || (user.Authorized != true && user.Admin != true){ ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "user not found or not authorized"}) return } From c7206d8dee9581d45a77132c31f415995eff0e91 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 14 Jan 2026 09:25:36 +0100 Subject: [PATCH 3/4] feat: add middleware to check admin access --- middleware/admin.go | 45 ++++++++++++++++++++++- middleware/authentification.go | 58 ------------------------------ middleware/common.go | 65 ++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 middleware/common.go diff --git a/middleware/admin.go b/middleware/admin.go index 34deae7..b36621c 100644 --- a/middleware/admin.go +++ b/middleware/admin.go @@ -1,7 +1,50 @@ package middleware -import "github.com/gin-gonic/gin" +import ( + "net/http" + "strings" + + "github.com/ValianceTekProject/AreaBack/db" + "github.com/ValianceTekProject/AreaBack/initializers" + "github.com/gin-gonic/gin" +) func CheckAdminAccess(ctx *gin.Context) { + authHeader := ctx.GetHeader("Authorization") + if authHeader == "" { + ctx.AbortWithStatus(http.StatusUnauthorized) + return + } + + const bearerPrefix = "Bearer " + if len(authHeader) < len(bearerPrefix) || authHeader[:len(bearerPrefix)] != bearerPrefix { + ctx.AbortWithStatus(http.StatusUnauthorized) + return + } + + token := strings.TrimPrefix(authHeader, bearerPrefix) + + claims, err := ValidateJWTToken(token) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) + return + } + + userID, ok := claims["sub"].(string) + if !ok { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"}) + return + } + + user, err := initializers.DB.Users.FindUnique( + db.Users.ID.Equals(userID), + ).Exec(ctx) + + if err != nil || user == nil || user.Admin != true { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "user not found or not authorized"}) + return + } + ctx.Set("user", user) + ctx.Next() } diff --git a/middleware/authentification.go b/middleware/authentification.go index c558f36..1106bad 100644 --- a/middleware/authentification.go +++ b/middleware/authentification.go @@ -1,43 +1,14 @@ package middleware import ( - "fmt" "net/http" - "os" "strings" "github.com/ValianceTekProject/AreaBack/db" "github.com/ValianceTekProject/AreaBack/initializers" "github.com/gin-gonic/gin" - "github.com/golang-jwt/jwt/v5" ) -func verifyToken(secret []byte) jwt.Keyfunc { - return func(token *jwt.Token) (any, error) { - _, ok := token.Method.(*jwt.SigningMethodHMAC) - if !ok { - return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) - } - return secret, nil - } -} - -func ValidateJWTToken(tokenString string) (jwt.MapClaims, error) { - secret := []byte(os.Getenv(("JWT_SECRET"))) - - token, err := jwt.Parse(tokenString, verifyToken(secret)) - if err != nil { - return nil, fmt.Errorf("invalid token: %w", err) - } - - claims, ok := token.Claims.(jwt.MapClaims) - if !ok || !token.Valid { - return nil, fmt.Errorf("invalid token claims") - } - - return claims, nil -} - func CheckUserAccess(ctx *gin.Context) { authHeader := ctx.GetHeader("Authorization") if authHeader == "" { @@ -77,32 +48,3 @@ func CheckUserAccess(ctx *gin.Context) { ctx.Set("user", user) ctx.Next() } - -func VerifyOauthUser(ctx *gin.Context) { - authHeader := ctx.GetHeader("Authorization") - if authHeader == "" { - ctx.Next() - return - } - - const bearerPrefix = "Bearer " - if len(authHeader) < len(bearerPrefix) || authHeader[:len(bearerPrefix)] != bearerPrefix { - ctx.Next() - return - } - - token := strings.TrimPrefix(authHeader, bearerPrefix) - - claims, err := ValidateJWTToken(token) - if err != nil { - ctx.Next() - return - } - userID, ok := claims["sub"].(string) - if !ok { - ctx.Next() - return - } - ctx.Set("userID", userID) - ctx.Next() -} diff --git a/middleware/common.go b/middleware/common.go new file mode 100644 index 0000000..ce3cc21 --- /dev/null +++ b/middleware/common.go @@ -0,0 +1,65 @@ +package middleware + +import ( + "fmt" + "os" + "strings" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" +) + +func verifyToken(secret []byte) jwt.Keyfunc { + return func(token *jwt.Token) (any, error) { + _, ok := token.Method.(*jwt.SigningMethodHMAC) + if !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + return secret, nil + } +} + +func ValidateJWTToken(tokenString string) (jwt.MapClaims, error) { + secret := []byte(os.Getenv(("JWT_SECRET"))) + + token, err := jwt.Parse(tokenString, verifyToken(secret)) + if err != nil { + return nil, fmt.Errorf("invalid token: %w", err) + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok || !token.Valid { + return nil, fmt.Errorf("invalid token claims") + } + + return claims, nil +} + +func VerifyOauthUser(ctx *gin.Context) { + authHeader := ctx.GetHeader("Authorization") + if authHeader == "" { + ctx.Next() + return + } + + const bearerPrefix = "Bearer " + if len(authHeader) < len(bearerPrefix) || authHeader[:len(bearerPrefix)] != bearerPrefix { + ctx.Next() + return + } + + token := strings.TrimPrefix(authHeader, bearerPrefix) + + claims, err := ValidateJWTToken(token) + if err != nil { + ctx.Next() + return + } + userID, ok := claims["sub"].(string) + if !ok { + ctx.Next() + return + } + ctx.Set("userID", userID) + ctx.Next() +} From daf6ff91e43b9d4127de1711b5abf2e94c8296c6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 14 Jan 2026 09:41:09 +0100 Subject: [PATCH 4/4] feat: add route to update user authorization --- controller/admin.go | 37 +++++++++++++++++++++++++++++++++++++ model/areas.go | 4 ++++ router/router.go | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 controller/admin.go diff --git a/controller/admin.go b/controller/admin.go new file mode 100644 index 0000000..4c83bf7 --- /dev/null +++ b/controller/admin.go @@ -0,0 +1,37 @@ +package controller + +import ( + "net/http" + + "github.com/ValianceTekProject/AreaBack/db" + "github.com/ValianceTekProject/AreaBack/initializers" + "github.com/ValianceTekProject/AreaBack/model" + "github.com/gin-gonic/gin" +) + +func UpdateUserStatus(ctx *gin.Context) { + userId := ctx.Param("userId") + + if userId == "" { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "User ID is required"}) + return + } + var payload model.UserUpdateStatusPayload + if err := ctx.ShouldBindJSON(&payload); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()}) + return + } + + updatedUser, err := initializers.DB.Users.FindUnique( + db.Users.ID.Equals(userId), + ).Update( + db.Users.Authorized.Set(payload.Authorized), + ).Exec(ctx) + + if err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user authorization", "details": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, updatedUser) +} diff --git a/model/areas.go b/model/areas.go index 374273f..f98b065 100644 --- a/model/areas.go +++ b/model/areas.go @@ -50,6 +50,10 @@ type AreaUpdateStatusPayload struct { IsEnabled bool `json:"is_enabled"` } +type UserUpdateStatusPayload struct { + Authorized bool `json:"authorized"` +} + type AreaInput struct { Name string `json:"name"` } diff --git a/router/router.go b/router/router.go index e87fa66..a61fef7 100644 --- a/router/router.go +++ b/router/router.go @@ -68,7 +68,7 @@ func setupProtectedRouter(router *gin.Engine) *gin.Engine { } protectedRoute.Use(middleware.CheckAdminAccess) { - // protectedRoute.PATCH("/areas/:userId/status", controller.UpdateUserStatus) + protectedRoute.PATCH("/users/:userId/status", controller.UpdateUserStatus) } return router }