diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 53df14f..4c2b410 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -20,21 +20,34 @@ func (h *Handler) InitRoutes() *gin.Engine { router := gin.New() serviceRouter := router.Group("/auth-service") { + // ========== Публичные эндпоинты (без авторизации) ========== auth := serviceRouter.Group("/auth") { auth.POST("/sign-up", h.signUp) auth.POST("/sign-in", h.signIn) auth.POST("/refresh", h.refresh) } - api := router.Group("/api") + + // ========== Защищенные эндпоинты ========== + api := serviceRouter.Group("/api") { - users := api.Group("/users", h.checkAdminIdentity) + // Группа /users - базовая авторизация на всей группе + users := api.Group("/users", h.userIdentity) { - users.POST("/:username", h.changeUserRole) + // GET /api/users - любой авторизованный пользователь + users.GET("", h.getAllUsers) + + // GET /api/users/:username - любой авторизованный пользователь + users.GET("/:username", h.getUserByUsername) + + // POST /api/users/:username - ТОЛЬКО администраторы + users.POST("/:username", h.requireAdmin(), h.changeUserRole) + + // DELETE /api/users/:username - ТОЛЬКО администраторы + users.DELETE("/:username", h.requireAdmin(), h.deleteUser) } } } return router } - \ No newline at end of file diff --git a/internal/handler/middleware.go b/internal/handler/middleware.go index 0083c71..4079584 100644 --- a/internal/handler/middleware.go +++ b/internal/handler/middleware.go @@ -6,14 +6,88 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" ) const ( authorizationHeader = "Authorization" - roleKey = "user_role" + userRoleKey = "user_role" + userIdKey = "user_id" ) +// userIdentity - базовый middleware, извлекает пользователя из токена +// Не проверяет роли, только валидность токена +func (h *Handler) userIdentity(c *gin.Context) { + header := c.GetHeader(authorizationHeader) + if header == "" { + newErrorResponse(c, http.StatusUnauthorized, "Пустой header авторизации") + c.Abort() + return + } + + headerParts := strings.Split(header, " ") + if len(headerParts) != 2 || headerParts[0] != "Bearer" { + newErrorResponse(c, http.StatusUnauthorized, "Невалидный формат токена") + c.Abort() + return + } + + userRole, err := h.services.ParseToken(headerParts[1]) + if err != nil { + newErrorResponse(c, http.StatusUnauthorized, "Невалидный токен") + c.Abort() + return + } + + c.Set(userRoleKey, userRole) + c.Next() +} + +// requireRole - middleware-фабрика, возвращает middleware для проверки конкретных ролей +func (h *Handler) requireRole(allowedRoles ...internal.UserRole) gin.HandlerFunc { + return func(c *gin.Context) { + userRole, exists := c.Get(userRoleKey) + if !exists { + newErrorResponse(c, http.StatusUnauthorized, "Пользователь не авторизован") + c.Abort() + return + } + + userRoleStr, ok := userRole.(string) + if !ok { + newErrorResponse(c, http.StatusInternalServerError, "Ошибка извлечения роли пользователя") + c.Abort() + return + } + + // Проверяем, есть ли роль пользователя в списке разрешенных + for _, allowedRole := range allowedRoles { + if userRoleStr == allowedRole.ToString() { + c.Next() + return + } + } + + newErrorResponse(c, http.StatusForbidden, "Недостаточно прав для выполнения запроса") + c.Abort() + } +} + +// Вспомогательные методы для удобства + +// requireAdmin - только администраторы +func (h *Handler) requireAdmin() gin.HandlerFunc { + return h.requireRole(internal.Admin) +} + +// requireTeacher - учителя и администраторы +func (h *Handler) requireTeacher() gin.HandlerFunc { + return h.requireRole(internal.Teacher, internal.Admin) +} + +func (h *Handler) requireStudent() gin.HandlerFunc { + return h.requireRole(internal.Student, internal.Teacher, internal.Admin) +} + func (h *Handler) checkAdminIdentity(c *gin.Context) { header := c.GetHeader(authorizationHeader) if header == "" { @@ -28,7 +102,7 @@ func (h *Handler) checkAdminIdentity(c *gin.Context) { } userRole, err := h.services.ParseToken(headerParts[1]) - + if userRole != string(internal.Admin) { newErrorResponse(c, http.StatusUnauthorized, "Недостаточно прав для выполнения запроса") return @@ -38,5 +112,5 @@ func (h *Handler) checkAdminIdentity(c *gin.Context) { return } - c.Set(roleKey, userRole) + c.Set(userRoleKey, userRole) } diff --git a/internal/handler/users.go b/internal/handler/users.go index 475e6f6..700dfe7 100644 --- a/internal/handler/users.go +++ b/internal/handler/users.go @@ -10,6 +10,7 @@ type ChangeUserRoleRequest struct { Role string `json:"role"` } +// changeUserRole - изменение роли пользователя (только для админов) func (h *Handler) changeUserRole(c *gin.Context) { var input ChangeUserRoleRequest @@ -34,3 +35,41 @@ func (h *Handler) changeUserRole(c *gin.Context) { "newRole": role, }) } + +// Примеры других handler'ов для демонстрации использования middleware + +// getAllUsers - получить список всех пользователей (доступно всем авторизованным) +func (h *Handler) getAllUsers(c *gin.Context) { + // Получаем роль текущего пользователя из контекста + userRole, _ := c.Get(userRoleKey) + + c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Список пользователей", + "your_role": userRole, + "access_level": "любой авторизованный пользователь", + }) +} + +// getUserByUsername - получить пользователя по username (доступно всем авторизованным) +func (h *Handler) getUserByUsername(c *gin.Context) { + username := c.Param("username") + userRole, _ := c.Get(userRoleKey) + + c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Информация о пользователе", + "username": username, + "your_role": userRole, + "access_level": "любой авторизованный пользователь", + }) +} + +// deleteUser - удалить пользователя (только для админов) +func (h *Handler) deleteUser(c *gin.Context) { + username := c.Param("username") + + c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Пользователь удален", + "username": username, + "access_level": "только администраторы", + }) +}