diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index ec8a27aa..1ea41f4b 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -265,7 +265,7 @@ func main() { kratosAdminService := service.NewKratosAdminService() oryAdminProvider := service.NewOryProvider() - tenantService := service.NewTenantService(tenantRepo, userRepo, ketoOutboxRepo) + tenantService := service.NewTenantService(tenantRepo, userRepo, userGroupRepo, ketoOutboxRepo) userGroupService := service.NewUserGroupService(userGroupRepo, userRepo, tenantRepo, ketoService, ketoOutboxRepo, kratosAdminService) tenantService.SetKetoService(ketoService) // Keto 주입 diff --git a/backend/internal/service/tenant_service.go b/backend/internal/service/tenant_service.go index 3c7e06c5..aa71f52e 100644 --- a/backend/internal/service/tenant_service.go +++ b/backend/internal/service/tenant_service.go @@ -26,17 +26,19 @@ type TenantService interface { } type tenantService struct { - repo repository.TenantRepository - userRepo repository.UserRepository - keto KetoService - outboxRepo repository.KetoOutboxRepository + repo repository.TenantRepository + userRepo repository.UserRepository + userGroupRepo repository.UserGroupRepository + keto KetoService + outboxRepo repository.KetoOutboxRepository } -func NewTenantService(repo repository.TenantRepository, userRepo repository.UserRepository, outboxRepo repository.KetoOutboxRepository) TenantService { +func NewTenantService(repo repository.TenantRepository, userRepo repository.UserRepository, userGroupRepo repository.UserGroupRepository, outboxRepo repository.KetoOutboxRepository) TenantService { return &tenantService{ - repo: repo, - userRepo: userRepo, - outboxRepo: outboxRepo, + repo: repo, + userRepo: userRepo, + userGroupRepo: userGroupRepo, + outboxRepo: outboxRepo, } } @@ -110,6 +112,51 @@ func (s *tenantService) RegisterTenant(ctx context.Context, name, slug, tenantTy return nil, err } + // [New] Create Default User Groups + if s.userGroupRepo != nil { + groups := []struct { + Name string + Slug string + }{ + {Name: "임직원", Slug: "members"}, + {Name: "관리자", Slug: "admins"}, + } + + for _, g := range groups { + newGroup := &domain.UserGroup{ + TenantID: tenant.ID, + Name: g.Name, + Slug: g.Slug, + Description: tenant.Name + " " + g.Name + " 그룹", + } + if err := s.userGroupRepo.Create(ctx, newGroup); err == nil { + // Sync group to Keto via Outbox + if s.outboxRepo != nil { + _ = s.outboxRepo.Create(ctx, &domain.KetoOutbox{ + Namespace: "UserGroup", + Object: newGroup.ID, + Relation: "tenants", + Subject: "Tenant:" + tenant.ID, + Action: domain.KetoOutboxActionCreate, + }) + + // If this is the 'admins' group and we have a creatorID, add creator to this group + if g.Slug == "admins" && creatorID != "" { + _ = s.outboxRepo.Create(ctx, &domain.KetoOutbox{ + Namespace: "UserGroup", + Object: newGroup.ID, + Relation: "members", + Subject: "User:" + creatorID, + Action: domain.KetoOutboxActionCreate, + }) + } + } + } else { + slog.Error("Failed to create default group", "group", g.Slug, "tenant", tenant.ID, "error", err) + } + } + } + // [Keto] Sync hierarchy and ownership via Outbox if s.outboxRepo != nil { // Sync hierarchy @@ -209,6 +256,37 @@ func (s *tenantService) ApproveTenant(ctx context.Context, id string) error { return err } + // [New] Create Default User Groups upon approval + if s.userGroupRepo != nil { + groups := []struct { + Name string + Slug string + }{ + {Name: "임직원", Slug: "members"}, + {Name: "관리자", Slug: "admins"}, + } + + for _, g := range groups { + newGroup := &domain.UserGroup{ + TenantID: tenant.ID, + Name: g.Name, + Slug: g.Slug, + Description: tenant.Name + " " + g.Name + " 그룹", + } + if err := s.userGroupRepo.Create(ctx, newGroup); err == nil { + if s.outboxRepo != nil { + _ = s.outboxRepo.Create(ctx, &domain.KetoOutbox{ + Namespace: "UserGroup", + Object: newGroup.ID, + Relation: "tenants", + Subject: "Tenant:" + tenant.ID, + Action: domain.KetoOutboxActionCreate, + }) + } + } + } + } + // [Keto] Sync relation via Outbox if s.outboxRepo != nil { if adminEmail, ok := tenant.Config["adminEmail"].(string); ok && adminEmail != "" {