We have a role concept
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"bamort/database"
|
||||
"bamort/logger"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ListUsers returns all users (admin only)
|
||||
func ListUsers(c *gin.Context) {
|
||||
logger.Debug("Listing all users...")
|
||||
|
||||
var users []User
|
||||
if err := database.DB.Find(&users).Error; err != nil {
|
||||
logger.Error("Failed to fetch users: %s", err.Error())
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to fetch users")
|
||||
return
|
||||
}
|
||||
|
||||
// Remove password hashes from response
|
||||
for i := range users {
|
||||
users[i].PasswordHash = ""
|
||||
users[i].ResetPwHash = nil
|
||||
}
|
||||
|
||||
logger.Info("Successfully fetched %d users", len(users))
|
||||
c.JSON(http.StatusOK, users)
|
||||
}
|
||||
|
||||
// GetUser returns a specific user by ID (admin only, or own profile)
|
||||
func GetUser(c *gin.Context) {
|
||||
logger.Debug("Fetching user by ID...")
|
||||
|
||||
userIDParam := c.Param("id")
|
||||
targetUserID, err := strconv.ParseUint(userIDParam, 10, 32)
|
||||
if err != nil {
|
||||
logger.Error("Invalid user ID: %s", userIDParam)
|
||||
respondWithError(c, http.StatusBadRequest, "Invalid user ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Get requesting user from context
|
||||
requestingUserInterface, exists := c.Get("user")
|
||||
if !exists {
|
||||
logger.Error("User not found in context")
|
||||
respondWithError(c, http.StatusUnauthorized, "Unauthorized")
|
||||
return
|
||||
}
|
||||
|
||||
requestingUser, ok := requestingUserInterface.(*User)
|
||||
if !ok {
|
||||
logger.Error("Invalid user context")
|
||||
respondWithError(c, http.StatusInternalServerError, "Invalid user context")
|
||||
return
|
||||
}
|
||||
|
||||
// Allow users to view their own profile, or admins to view any profile
|
||||
if requestingUser.UserID != uint(targetUserID) && !requestingUser.IsAdmin() {
|
||||
logger.Warn("User %s attempted to access user %d without permission", requestingUser.Username, targetUserID)
|
||||
respondWithError(c, http.StatusForbidden, "Forbidden")
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := user.FirstId(uint(targetUserID)); err != nil {
|
||||
logger.Error("User not found: %d", targetUserID)
|
||||
respondWithError(c, http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Remove sensitive data
|
||||
user.PasswordHash = ""
|
||||
user.ResetPwHash = nil
|
||||
|
||||
logger.Info("Successfully fetched user: %s (ID: %d)", user.Username, user.UserID)
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
// UpdateUserRole updates a user's role (admin only)
|
||||
func UpdateUserRole(c *gin.Context) {
|
||||
logger.Debug("Updating user role...")
|
||||
|
||||
userIDParam := c.Param("id")
|
||||
targetUserID, err := strconv.ParseUint(userIDParam, 10, 32)
|
||||
if err != nil {
|
||||
logger.Error("Invalid user ID: %s", userIDParam)
|
||||
respondWithError(c, http.StatusBadRequest, "Invalid user ID")
|
||||
return
|
||||
}
|
||||
|
||||
var input struct {
|
||||
Role string `json:"role" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
logger.Error("Failed to parse role update data: %s", err.Error())
|
||||
respondWithError(c, http.StatusBadRequest, "Role is required")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate role
|
||||
if !ValidateRole(input.Role) {
|
||||
logger.Error("Invalid role: %s", input.Role)
|
||||
respondWithError(c, http.StatusBadRequest, fmt.Sprintf("Invalid role. Must be one of: %s, %s, %s", RoleStandardUser, RoleMaintainer, RoleAdmin))
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := user.FirstId(uint(targetUserID)); err != nil {
|
||||
logger.Error("User not found: %d", targetUserID)
|
||||
respondWithError(c, http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Get requesting user for logging
|
||||
requestingUserInterface, _ := c.Get("user")
|
||||
requestingUser, _ := requestingUserInterface.(*User)
|
||||
|
||||
oldRole := user.Role
|
||||
user.Role = input.Role
|
||||
|
||||
if err := user.Save(); err != nil {
|
||||
logger.Error("Failed to update user role for user %s: %s", user.Username, err.Error())
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to update user role")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("User role updated: %s (ID: %d) from %s to %s by %s", user.Username, user.UserID, oldRole, user.Role, requestingUser.Username)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "User role updated successfully",
|
||||
"user": gin.H{
|
||||
"id": user.UserID,
|
||||
"username": user.Username,
|
||||
"role": user.Role,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteUser deletes a user (admin only)
|
||||
func DeleteUser(c *gin.Context) {
|
||||
logger.Debug("Deleting user...")
|
||||
|
||||
userIDParam := c.Param("id")
|
||||
targetUserID, err := strconv.ParseUint(userIDParam, 10, 32)
|
||||
if err != nil {
|
||||
logger.Error("Invalid user ID: %s", userIDParam)
|
||||
respondWithError(c, http.StatusBadRequest, "Invalid user ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Get requesting user
|
||||
requestingUserInterface, exists := c.Get("user")
|
||||
if !exists {
|
||||
logger.Error("User not found in context")
|
||||
respondWithError(c, http.StatusUnauthorized, "Unauthorized")
|
||||
return
|
||||
}
|
||||
|
||||
requestingUser, ok := requestingUserInterface.(*User)
|
||||
if !ok {
|
||||
logger.Error("Invalid user context")
|
||||
respondWithError(c, http.StatusInternalServerError, "Invalid user context")
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent self-deletion
|
||||
if requestingUser.UserID == uint(targetUserID) {
|
||||
logger.Warn("User %s attempted to delete themselves", requestingUser.Username)
|
||||
respondWithError(c, http.StatusBadRequest, "Cannot delete your own account")
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := user.FirstId(uint(targetUserID)); err != nil {
|
||||
logger.Error("User not found: %d", targetUserID)
|
||||
respondWithError(c, http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Delete(&user).Error; err != nil {
|
||||
logger.Error("Failed to delete user %s: %s", user.Username, err.Error())
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to delete user")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("User deleted: %s (ID: %d) by %s", user.Username, user.UserID, requestingUser.Username)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "User deleted successfully",
|
||||
})
|
||||
}
|
||||
@@ -60,6 +60,11 @@ func RegisterUser(c *gin.Context) {
|
||||
user.PasswordHash = hex.EncodeToString(hashedPassword[:])
|
||||
logger.Debug("Passwort-Hash erstellt für Benutzer: %s", user.Username)
|
||||
|
||||
// Set default role for new users
|
||||
if user.Role == "" {
|
||||
user.Role = RoleStandardUser
|
||||
}
|
||||
|
||||
//fmt.Printf("pwdh: %s", user.PasswordHash)
|
||||
if err := user.Create(); err != nil {
|
||||
logger.Error("Fehler beim Erstellen des Benutzers %s: %s", user.Username, err.Error())
|
||||
@@ -217,6 +222,7 @@ func AuthMiddleware() gin.HandlerFunc {
|
||||
// Set user information in context
|
||||
c.Set("userID", user.UserID)
|
||||
c.Set("username", user.Username)
|
||||
c.Set("user", user)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
@@ -438,6 +444,7 @@ func GetUserProfile(c *gin.Context) {
|
||||
"id": user.UserID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"role": user.Role,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RequireRole is a middleware that checks if the user has the required role
|
||||
func RequireRole(requiredRole string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Get user from context (set by auth middleware)
|
||||
userInterface, exists := c.Get("user")
|
||||
if !exists {
|
||||
respondWithError(c, http.StatusUnauthorized, "Unauthorized")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
user, ok := userInterface.(*User)
|
||||
if !ok {
|
||||
respondWithError(c, http.StatusInternalServerError, "Invalid user context")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if user has required role
|
||||
switch requiredRole {
|
||||
case RoleAdmin:
|
||||
if !user.IsAdmin() {
|
||||
respondWithError(c, http.StatusForbidden, "Admin role required")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
case RoleMaintainer:
|
||||
if !user.IsMaintainer() {
|
||||
respondWithError(c, http.StatusForbidden, "Maintainer role required")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
case RoleStandardUser:
|
||||
if !user.IsStandardUser() {
|
||||
respondWithError(c, http.StatusForbidden, "Insufficient permissions")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
default:
|
||||
respondWithError(c, http.StatusInternalServerError, "Invalid role requirement")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAdmin is a convenience middleware for admin-only endpoints
|
||||
func RequireAdmin() gin.HandlerFunc {
|
||||
return RequireRole(RoleAdmin)
|
||||
}
|
||||
|
||||
// RequireMaintainer is a convenience middleware for maintainer-or-higher endpoints
|
||||
func RequireMaintainer() gin.HandlerFunc {
|
||||
return RequireRole(RoleMaintainer)
|
||||
}
|
||||
@@ -8,11 +8,19 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Role constants
|
||||
const (
|
||||
RoleStandardUser = "standard"
|
||||
RoleMaintainer = "maintainer"
|
||||
RoleAdmin = "admin"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UserID uint `gorm:"primaryKey" json:"id"`
|
||||
Username string `gorm:"unique" json:"username"`
|
||||
PasswordHash string `json:"password"`
|
||||
Email string `gorm:"unique" json:"email"`
|
||||
Role string `gorm:"default:standard" json:"role"`
|
||||
ResetPwHash *string `gorm:"index" json:"-"` // Hash für Password-Reset (wird nicht serialisiert)
|
||||
ResetPwHashExpires *time.Time `json:"-"` // Ablaufzeit für Password-Reset-Hash
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@@ -120,3 +128,28 @@ func (object *User) IsResetHashValid(resetHash string) bool {
|
||||
}
|
||||
return *object.ResetPwHash == resetHash && time.Now().Before(*object.ResetPwHashExpires)
|
||||
}
|
||||
|
||||
// HasRole checks if the user has the specified role
|
||||
func (u *User) HasRole(role string) bool {
|
||||
return u.Role == role
|
||||
}
|
||||
|
||||
// IsAdmin checks if the user is an admin
|
||||
func (u *User) IsAdmin() bool {
|
||||
return u.Role == RoleAdmin
|
||||
}
|
||||
|
||||
// IsMaintainer checks if the user is a maintainer or higher
|
||||
func (u *User) IsMaintainer() bool {
|
||||
return u.Role == RoleMaintainer || u.Role == RoleAdmin
|
||||
}
|
||||
|
||||
// IsStandardUser checks if the user is a standard user or higher
|
||||
func (u *User) IsStandardUser() bool {
|
||||
return u.Role == RoleStandardUser || u.IsMaintainer()
|
||||
}
|
||||
|
||||
// ValidateRole checks if the given role is valid
|
||||
func ValidateRole(role string) bool {
|
||||
return role == RoleStandardUser || role == RoleMaintainer || role == RoleAdmin
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"bamort/database"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// setupRoleTestEnvironment sets up the test environment for role tests
|
||||
func setupRoleTestEnvironment(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
database.SetupTestDB()
|
||||
err := MigrateStructure()
|
||||
require.NoError(t, err, "Should migrate user structure")
|
||||
gin.SetMode(gin.TestMode)
|
||||
}
|
||||
|
||||
// TestUserRoleDefaults tests that new users get standard role by default
|
||||
func TestUserRoleDefaults(t *testing.T) {
|
||||
setupRoleTestEnvironment(t)
|
||||
|
||||
user := &User{
|
||||
Username: "roletest_user",
|
||||
PasswordHash: "hashedpw",
|
||||
Email: "roletest@example.com",
|
||||
}
|
||||
|
||||
err := user.Create()
|
||||
require.NoError(t, err, "Should create user")
|
||||
assert.Equal(t, RoleStandardUser, user.Role, "New users should have standard role")
|
||||
}
|
||||
|
||||
// TestRoleValidation tests role validation
|
||||
func TestRoleValidation(t *testing.T) {
|
||||
assert.True(t, ValidateRole(RoleStandardUser), "standard should be valid")
|
||||
assert.True(t, ValidateRole(RoleMaintainer), "maintainer should be valid")
|
||||
assert.True(t, ValidateRole(RoleAdmin), "admin should be valid")
|
||||
assert.False(t, ValidateRole("invalid"), "invalid should not be valid")
|
||||
assert.False(t, ValidateRole(""), "empty should not be valid")
|
||||
}
|
||||
|
||||
// TestRoleHierarchy tests role hierarchy methods
|
||||
func TestRoleHierarchy(t *testing.T) {
|
||||
standardUser := &User{Role: RoleStandardUser}
|
||||
maintainer := &User{Role: RoleMaintainer}
|
||||
admin := &User{Role: RoleAdmin}
|
||||
|
||||
// Test IsStandardUser
|
||||
assert.True(t, standardUser.IsStandardUser(), "standard user should pass IsStandardUser")
|
||||
assert.True(t, maintainer.IsStandardUser(), "maintainer should pass IsStandardUser")
|
||||
assert.True(t, admin.IsStandardUser(), "admin should pass IsStandardUser")
|
||||
|
||||
// Test IsMaintainer
|
||||
assert.False(t, standardUser.IsMaintainer(), "standard user should not pass IsMaintainer")
|
||||
assert.True(t, maintainer.IsMaintainer(), "maintainer should pass IsMaintainer")
|
||||
assert.True(t, admin.IsMaintainer(), "admin should pass IsMaintainer")
|
||||
|
||||
// Test IsAdmin
|
||||
assert.False(t, standardUser.IsAdmin(), "standard user should not pass IsAdmin")
|
||||
assert.False(t, maintainer.IsAdmin(), "maintainer should not pass IsAdmin")
|
||||
assert.True(t, admin.IsAdmin(), "admin should pass IsAdmin")
|
||||
}
|
||||
|
||||
// TestListUsers tests listing all users (admin only)
|
||||
func TestListUsers(t *testing.T) {
|
||||
setupRoleTestEnvironment(t)
|
||||
|
||||
// Create test users
|
||||
admin := &User{Username: "listadmin", PasswordHash: "hash", Email: "listadmin@test.com", Role: RoleAdmin}
|
||||
require.NoError(t, admin.Create())
|
||||
|
||||
standardUser := &User{Username: "listuser", PasswordHash: "hash", Email: "listuser@test.com", Role: RoleStandardUser}
|
||||
require.NoError(t, standardUser.Create())
|
||||
|
||||
// Test admin access
|
||||
router := gin.Default()
|
||||
router.GET("/users", func(c *gin.Context) {
|
||||
c.Set("user", admin)
|
||||
}, RequireAdmin(), ListUsers)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/users", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code, "Admin should access list")
|
||||
|
||||
// Test standard user access (should fail)
|
||||
router2 := gin.Default()
|
||||
router2.GET("/users", func(c *gin.Context) {
|
||||
c.Set("user", standardUser)
|
||||
}, RequireAdmin(), ListUsers)
|
||||
|
||||
req2, _ := http.NewRequest("GET", "/users", nil)
|
||||
w2 := httptest.NewRecorder()
|
||||
router2.ServeHTTP(w2, req2)
|
||||
|
||||
assert.Equal(t, http.StatusForbidden, w2.Code, "Standard user should not access list")
|
||||
}
|
||||
|
||||
// TestUpdateUserRole tests updating user role (admin only)
|
||||
func TestUpdateUserRole(t *testing.T) {
|
||||
setupRoleTestEnvironment(t)
|
||||
|
||||
// Create admin and target user
|
||||
admin := &User{Username: "roleadmin", PasswordHash: "hash", Email: "roleadmin@test.com", Role: RoleAdmin}
|
||||
require.NoError(t, admin.Create())
|
||||
|
||||
targetUser := &User{Username: "roletarget", PasswordHash: "hash", Email: "roletarget@test.com", Role: RoleStandardUser}
|
||||
require.NoError(t, targetUser.Create())
|
||||
|
||||
// Test valid role update
|
||||
router := gin.Default()
|
||||
router.PUT("/users/:id/role", func(c *gin.Context) {
|
||||
c.Set("user", admin)
|
||||
}, RequireAdmin(), UpdateUserRole)
|
||||
|
||||
updateData := map[string]string{"role": RoleMaintainer}
|
||||
jsonData, _ := json.Marshal(updateData)
|
||||
|
||||
req, _ := http.NewRequest("PUT", fmt.Sprintf("/users/%d/role", targetUser.UserID), bytes.NewBuffer(jsonData))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code, "Admin should update role")
|
||||
|
||||
// Verify role was updated
|
||||
var updatedUser User
|
||||
require.NoError(t, updatedUser.FirstId(targetUser.UserID))
|
||||
assert.Equal(t, RoleMaintainer, updatedUser.Role, "Role should be updated to maintainer")
|
||||
|
||||
// Test invalid role update
|
||||
invalidData := map[string]string{"role": "invalid"}
|
||||
jsonData2, _ := json.Marshal(invalidData)
|
||||
|
||||
req2, _ := http.NewRequest("PUT", fmt.Sprintf("/users/%d/role", targetUser.UserID), bytes.NewBuffer(jsonData2))
|
||||
req2.Header.Set("Content-Type", "application/json")
|
||||
w2 := httptest.NewRecorder()
|
||||
router.ServeHTTP(w2, req2)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w2.Code, "Should reject invalid role")
|
||||
}
|
||||
|
||||
// TestDeleteUser tests deleting a user (admin only)
|
||||
func TestDeleteUser(t *testing.T) {
|
||||
setupRoleTestEnvironment(t)
|
||||
|
||||
// Create admin and target user
|
||||
admin := &User{Username: "deladmin", PasswordHash: "hash", Email: "deladmin@test.com", Role: RoleAdmin}
|
||||
require.NoError(t, admin.Create())
|
||||
|
||||
targetUser := &User{Username: "deltarget", PasswordHash: "hash", Email: "deltarget@test.com", Role: RoleStandardUser}
|
||||
require.NoError(t, targetUser.Create())
|
||||
|
||||
// Test deletion
|
||||
router := gin.Default()
|
||||
router.DELETE("/users/:id", func(c *gin.Context) {
|
||||
c.Set("user", admin)
|
||||
}, RequireAdmin(), DeleteUser)
|
||||
|
||||
req, _ := http.NewRequest("DELETE", fmt.Sprintf("/users/%d", targetUser.UserID), nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code, "Admin should delete user")
|
||||
|
||||
// Verify user was deleted
|
||||
var deletedUser User
|
||||
err := deletedUser.FirstId(targetUser.UserID)
|
||||
assert.Error(t, err, "User should not exist after deletion")
|
||||
}
|
||||
|
||||
// TestMaintainerPermissions tests maintainer-specific permissions
|
||||
func TestMaintainerPermissions(t *testing.T) {
|
||||
setupRoleTestEnvironment(t)
|
||||
|
||||
standardUser := &User{Username: "permuser", PasswordHash: "hash", Email: "permuser@test.com", Role: RoleStandardUser}
|
||||
require.NoError(t, standardUser.Create())
|
||||
|
||||
maintainer := &User{Username: "permmaintainer", PasswordHash: "hash", Email: "permmaintainer@test.com", Role: RoleMaintainer}
|
||||
require.NoError(t, maintainer.Create())
|
||||
|
||||
admin := &User{Username: "permadmin", PasswordHash: "hash", Email: "permadmin@test.com", Role: RoleAdmin}
|
||||
require.NoError(t, admin.Create())
|
||||
|
||||
router := gin.Default()
|
||||
router.GET("/maintainer-only", func(c *gin.Context) {
|
||||
c.Set("user", standardUser)
|
||||
}, RequireMaintainer(), func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
})
|
||||
|
||||
// Standard user should fail
|
||||
req, _ := http.NewRequest("GET", "/maintainer-only", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusForbidden, w.Code, "Standard user should not access")
|
||||
|
||||
// Maintainer should succeed
|
||||
router2 := gin.Default()
|
||||
router2.GET("/maintainer-only", func(c *gin.Context) {
|
||||
c.Set("user", maintainer)
|
||||
}, RequireMaintainer(), func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
})
|
||||
|
||||
req2, _ := http.NewRequest("GET", "/maintainer-only", nil)
|
||||
w2 := httptest.NewRecorder()
|
||||
router2.ServeHTTP(w2, req2)
|
||||
assert.Equal(t, http.StatusOK, w2.Code, "Maintainer should access")
|
||||
|
||||
// Admin should also succeed
|
||||
router3 := gin.Default()
|
||||
router3.GET("/maintainer-only", func(c *gin.Context) {
|
||||
c.Set("user", admin)
|
||||
}, RequireMaintainer(), func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
})
|
||||
|
||||
req3, _ := http.NewRequest("GET", "/maintainer-only", nil)
|
||||
w3 := httptest.NewRecorder()
|
||||
router3.ServeHTTP(w3, req3)
|
||||
assert.Equal(t, http.StatusOK, w3.Code, "Admin should access maintainer endpoints")
|
||||
}
|
||||
@@ -13,4 +13,14 @@ func RegisterRoutes(r *gin.RouterGroup) {
|
||||
userGroup.PUT("/email", UpdateEmail)
|
||||
userGroup.PUT("/password", UpdatePassword)
|
||||
}
|
||||
|
||||
// Admin routes - require admin role
|
||||
adminGroup := r.Group("/users")
|
||||
adminGroup.Use(RequireAdmin())
|
||||
{
|
||||
adminGroup.GET("", ListUsers)
|
||||
adminGroup.GET("/:id", GetUser)
|
||||
adminGroup.PUT("/:id/role", UpdateUserRole)
|
||||
adminGroup.DELETE("/:id", DeleteUser)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user