package service import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/repository" "context" "log/slog" ) type TenantGroupService interface { CreateGroup(ctx context.Context, name, slug, description string) (*domain.TenantGroup, error) GetGroup(ctx context.Context, id string) (*domain.TenantGroup, error) ListGroups(ctx context.Context, limit, offset int) ([]domain.TenantGroup, int64, error) UpdateGroup(ctx context.Context, id string, name, description string) (*domain.TenantGroup, error) DeleteGroup(ctx context.Context, id string) error AddTenantToGroup(ctx context.Context, groupID, tenantID string) error RemoveTenantFromGroup(ctx context.Context, groupID, tenantID string) error } type tenantGroupService struct { repo repository.TenantGroupRepository keto KetoService } func NewTenantGroupService(repo repository.TenantGroupRepository, keto KetoService) TenantGroupService { return &tenantGroupService{repo: repo, keto: keto} } func (s *tenantGroupService) CreateGroup(ctx context.Context, name, slug, description string) (*domain.TenantGroup, error) { group := &domain.TenantGroup{ Name: name, Slug: slug, Description: description, } if err := s.repo.Create(ctx, group); err != nil { return nil, err } return group, nil } func (s *tenantGroupService) GetGroup(ctx context.Context, id string) (*domain.TenantGroup, error) { return s.repo.FindByID(ctx, id) } func (s *tenantGroupService) ListGroups(ctx context.Context, limit, offset int) ([]domain.TenantGroup, int64, error) { return s.repo.List(ctx, limit, offset) } func (s *tenantGroupService) UpdateGroup(ctx context.Context, id string, name, description string) (*domain.TenantGroup, error) { group, err := s.repo.FindByID(ctx, id) if err != nil { return nil, err } group.Name = name group.Description = description if err := s.repo.Update(ctx, group); err != nil { return nil, err } return group, nil } func (s *tenantGroupService) DeleteGroup(ctx context.Context, id string) error { return s.repo.Delete(ctx, id) } func (s *tenantGroupService) AddTenantToGroup(ctx context.Context, groupID, tenantID string) error { if err := s.repo.AddTenant(ctx, groupID, tenantID); err != nil { return err } // [Keto] ReBAC: Tenant -> Group membership if s.keto != nil { err := s.keto.CreateRelation(ctx, "Tenant", tenantID, "parent_group", groupID) if err != nil { slog.Error("Failed to sync Keto relation for tenant group", "tenantID", tenantID, "groupID", groupID, "error", err) } } return nil } func (s *tenantGroupService) RemoveTenantFromGroup(ctx context.Context, groupID, tenantID string) error { if err := s.repo.RemoveTenant(ctx, groupID, tenantID); err != nil { return err } // [Keto] ReBAC: Remove Tenant -> Group membership if s.keto != nil { err := s.keto.DeleteRelation(ctx, "Tenant", tenantID, "parent_group", groupID) if err != nil { slog.Error("Failed to remove Keto relation for tenant group", "tenantID", tenantID, "groupID", groupID, "error", err) } } return nil }