veza/veza-backend-api/internal/services/role_service.go
2025-12-03 20:29:37 +01:00

155 lines
5.3 KiB
Go

package services
import (
"context"
"errors"
"fmt"
"github.com/google/uuid"
"time"
"veza-backend-api/internal/models"
"gorm.io/gorm"
)
// RoleService gère les rôles et permissions
type RoleService struct {
db *gorm.DB
}
// NewRoleService crée un nouveau service de rôles
func NewRoleService(db *gorm.DB) *RoleService {
return &RoleService{db: db}
}
// GetRoles récupère tous les rôles avec leurs permissions
func (s *RoleService) GetRoles(ctx context.Context) ([]models.Role, error) {
var roles []models.Role
if err := s.db.WithContext(ctx).Preload("Permissions").Find(&roles).Error; err != nil {
return nil, fmt.Errorf("failed to get roles: %w", err)
}
return roles, nil
}
// GetRole récupère un rôle par son ID avec ses permissions
func (s *RoleService) GetRole(ctx context.Context, roleID uuid.UUID) (*models.Role, error) {
var role models.Role
if err := s.db.WithContext(ctx).Preload("Permissions").First(&role, roleID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("role not found")
}
return nil, fmt.Errorf("failed to get role: %w", err)
}
return &role, nil
}
// CreateRole crée un nouveau rôle
func (s *RoleService) CreateRole(ctx context.Context, role *models.Role) error {
if err := s.db.WithContext(ctx).Create(role).Error; err != nil {
return fmt.Errorf("failed to create role: %w", err)
}
return nil
}
// UpdateRole met à jour un rôle (seulement les rôles non-système)
func (s *RoleService) UpdateRole(ctx context.Context, roleID uuid.UUID, updates *models.Role) error {
result := s.db.WithContext(ctx).Model(&models.Role{}).Where("id = ? AND is_system = ?", roleID, false).Updates(updates)
if result.Error != nil {
return fmt.Errorf("failed to update role: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("role not found or is system role")
}
return nil
}
// DeleteRole supprime un rôle (seulement les rôles non-système)
func (s *RoleService) DeleteRole(ctx context.Context, roleID uuid.UUID) error {
var role models.Role
if err := s.db.WithContext(ctx).First(&role, roleID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("role not found")
}
return fmt.Errorf("failed to get role: %w", err)
}
if role.IsSystem {
return fmt.Errorf("cannot delete system role")
}
if err := s.db.WithContext(ctx).Delete(&role).Error; err != nil {
return fmt.Errorf("failed to delete role: %w", err)
}
return nil
}
// AssignRoleToUser assigne un rôle à un utilisateur
// MIGRATION UUID: userID, roleID et assignedBy migrés vers uuid.UUID
func (s *RoleService) AssignRoleToUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID, assignedBy uuid.UUID, expiresAt *time.Time) error {
userRole := &models.UserRole{
UserID: userID,
RoleID: roleID,
AssignedBy: &assignedBy, // UUID
AssignedAt: time.Now(),
ExpiresAt: expiresAt,
IsActive: true,
}
if err := s.db.WithContext(ctx).Create(userRole).Error; err != nil {
return fmt.Errorf("failed to assign role: %w", err)
}
return nil
}
// RevokeRoleFromUser révoque un rôle d'un utilisateur
func (s *RoleService) RevokeRoleFromUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID) error {
result := s.db.WithContext(ctx).Model(&models.UserRole{}).
Where("user_id = ? AND role_id = ?", userID, roleID).
Update("is_active", false)
if result.Error != nil {
return fmt.Errorf("failed to revoke role: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("role assignment not found")
}
return nil
}
// GetUserRoles récupère tous les rôles actifs d'un utilisateur
func (s *RoleService) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]models.Role, error) {
var roles []models.Role
if err := s.db.WithContext(ctx).
Table("roles").
Joins("JOIN user_roles ON roles.id = user_roles.role_id").
Where("user_roles.user_id = ? AND user_roles.is_active = ?", userID, true).
Preload("Permissions").
Find(&roles).Error; err != nil {
return nil, fmt.Errorf("failed to get user roles: %w", err)
}
return roles, nil
}
// HasRole vérifie si un utilisateur a un rôle spécifique
func (s *RoleService) HasRole(ctx context.Context, userID uuid.UUID, roleName string) (bool, error) {
var count int64
if err := s.db.WithContext(ctx).
Table("user_roles").
Joins("JOIN roles ON user_roles.role_id = roles.id").
Where("user_roles.user_id = ? AND user_roles.is_active = ? AND roles.name = ?", userID, true, roleName).
Count(&count).Error; err != nil {
return false, fmt.Errorf("failed to check role: %w", err)
}
return count > 0, nil
}
// HasPermission vérifie si un utilisateur a une permission spécifique via ses rôles
func (s *RoleService) HasPermission(ctx context.Context, userID uuid.UUID, resource, action string) (bool, error) {
var count int64
if err := s.db.WithContext(ctx).
Table("permissions").
Joins("JOIN role_permissions ON permissions.id = role_permissions.permission_id").
Joins("JOIN user_roles ON role_permissions.role_id = user_roles.role_id").
Where("user_roles.user_id = ? AND user_roles.is_active = ? AND permissions.resource = ? AND permissions.action = ?",
userID, true, resource, action).
Count(&count).Error; err != nil {
return false, fmt.Errorf("failed to check permission: %w", err)
}
return count > 0, nil
}