package acl import ( "fmt" "gorm.io/gorm" ) type Service struct { initialized bool db *gorm.DB } func NewService(db *gorm.DB) (*Service, error) { if db == nil { return nil, fmt.Errorf("db is required") } return &Service{ db: db, }, nil } func (s *Service) isInitialized() bool { return s.initialized } func (s *Service) Init() error { if s.isInitialized() { return nil } // AutoMigrate models err := s.db.AutoMigrate(&UserRole{}, &Resource{}, &Role{}, &RoleResource{}) if err != nil { return fmt.Errorf("failed to migrate ACL models: %w", err) } s.initialized = true return nil } // Admin crud functions // CreateRole creates a new role with the given name func (s *Service) CreateRole(name string) error { if !s.isInitialized() { return fmt.Errorf("acl service is not initialized") } role := Role{Name: name} return s.db.FirstOrCreate(&role, &Role{Name: name}).Error } // CreateResource creates a new resource with the given key func (s *Service) CreateResource(key string) error { if !s.isInitialized() { return fmt.Errorf("acl service is not initialized") } res := Resource{Key: key} return s.db.FirstOrCreate(&res, &Resource{Key: key}).Error } // AssignResourceToRole assigns a resource to a role func (s *Service) AssignResourceToRole(roleID, resourceID uint) error { if !s.isInitialized() { return fmt.Errorf("acl service is not initialized") } rr := RoleResource{ RoleID: roleID, ResourceID: resourceID, } return s.db.FirstOrCreate(&rr, RoleResource{RoleID: roleID, ResourceID: resourceID}).Error } // AssignRoleToUser assigns a role to a user func (s *Service) AssignRoleToUser(roleID, userID uint) error { if !s.isInitialized() { return fmt.Errorf("acl service is not initialized") } ur := UserRole{ UserID: userID, RoleID: roleID, } return s.db.FirstOrCreate(&ur, UserRole{UserID: userID, RoleID: roleID}).Error } // RemoveResourceFromRole removes a resource from a role func (s *Service) RemoveResourceFromRole(roleID, resourceID uint) error { if !s.isInitialized() { return fmt.Errorf("acl service is not initialized") } return s.db.Where("role_id = ? AND resource_id = ?", roleID, resourceID).Delete(&RoleResource{}).Error } // RemoveRoleFromUser removes a role from a user func (s *Service) RemoveRoleFromUser(roleID, userID uint) error { if !s.isInitialized() { return fmt.Errorf("acl service is not initialized") } return s.db.Where("role_id = ? AND user_id = ?", roleID, userID).Delete(&UserRole{}).Error } // GetRoles returns all roles func (s *Service) GetRoles() ([]Role, error) { if !s.isInitialized() { return nil, fmt.Errorf("acl service is not initialized") } var roles []Role err := s.db.Preload("Resources").Order("id").Find(&roles).Error return roles, err } // GetPermissions returns all permissions func (s *Service) GetPermissions() ([]Resource, error) { if !s.isInitialized() { return nil, fmt.Errorf("acl service is not initialized") } var resources []Resource err := s.db.Order("id").Find(&resources).Error return resources, err } // GetRoleResources returns all resources for a given role func (s *Service) GetRoleResources(roleID uint) ([]Resource, error) { if !s.isInitialized() { return nil, fmt.Errorf("acl service is not initialized") } var resources []Resource err := s.db.Joins("JOIN role_resources rr ON rr.resource_id = resources.id"). Where("rr.role_id = ?", roleID).Find(&resources).Error return resources, err } // GetUserRoles returns all roles for a given user func (s *Service) GetUserRoles(userID uint) ([]Role, error) { if !s.isInitialized() { return nil, fmt.Errorf("acl service is not initialized") } var roles []Role err := s.db.Joins("JOIN user_roles ur ON ur.role_id = roles.id"). Where("ur.user_id = ?", userID).Find(&roles).Error return roles, err }