Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions controller/admin.go
Original file line number Diff line number Diff line change
@@ -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)
}
50 changes: 50 additions & 0 deletions middleware/admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package middleware

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()
}
62 changes: 2 additions & 60 deletions middleware/authentification.go
Original file line number Diff line number Diff line change
@@ -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 == "" {
Expand Down Expand Up @@ -69,40 +40,11 @@ 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 && user.Admin != true){
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "user not found or not authorized"})
return
}

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()
}
65 changes: 65 additions & 0 deletions middleware/common.go
Original file line number Diff line number Diff line change
@@ -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()
}
4 changes: 4 additions & 0 deletions model/areas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
Expand Down
4 changes: 4 additions & 0 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -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("/users/:userId/status", controller.UpdateUserStatus)
}
return router
}

Expand Down
2 changes: 2 additions & 0 deletions schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down