From 07f4c1258c165c445a489725c5a8fae2dd09039d Mon Sep 17 00:00:00 2001 From: kyy Date: Thu, 19 Mar 2026 13:05:13 +0900 Subject: [PATCH] =?UTF-8?q?=ED=85=8C=EB=84=8C=ED=8A=B8=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/cmd/server/main.go | 3 +- backend/internal/handler/dev_handler.go | 47 ++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index b5dbec68..137b7df9 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -277,7 +277,7 @@ func main() { auditHandler := handler.NewAuditHandler(auditRepo) authHandler := handler.NewAuthHandler(redisService, idpProvider, auditRepo, oathkeeperRepo, tenantService, ketoService, ketoOutboxRepo, userRepo, consentRepo, kratosAdminService) adminHandler := handler.NewAdminHandler(ketoService) - devHandler := handler.NewDevHandler(redisService, secretRepo, consentRepo, relyingPartyService, ketoService, authHandler) + devHandler := handler.NewDevHandler(redisService, secretRepo, consentRepo, relyingPartyService, ketoService, tenantService, authHandler) devHandler.AuditRepo = auditRepo tenantHandler := handler.NewTenantHandler(db, tenantService, userRepo, ketoService, ketoOutboxRepo, kratosAdminService) userGroupHandler := handler.NewUserGroupHandler(userGroupService) @@ -660,6 +660,7 @@ func main() { // 개발자 포털 라우트 (RP/Consent 관리 및 IdP 설정) dev := api.Group("/dev") dev.Get("/stats", devHandler.GetStats) + dev.Get("/my-tenants", devHandler.ListMyTenants) dev.Get("/clients", devHandler.ListClients) dev.Post("/clients", devHandler.CreateClient) dev.Get("/clients/:id", devHandler.GetClient) diff --git a/backend/internal/handler/dev_handler.go b/backend/internal/handler/dev_handler.go index 7f6103e8..ec3cb21a 100644 --- a/backend/internal/handler/dev_handler.go +++ b/backend/internal/handler/dev_handler.go @@ -30,6 +30,7 @@ type DevHandler struct { ConsentRepo repository.ClientConsentRepository Keto service.KetoService RPSvc service.RelyingPartyService + TenantSvc service.TenantService Auth interface { GetEnrichedProfile(c *fiber.Ctx) (*domain.UserProfileResponse, error) } @@ -40,7 +41,7 @@ func NewDevHandler( secretRepo domain.ClientSecretRepository, consentRepo repository.ClientConsentRepository, rpSvc service.RelyingPartyService, - keto service.KetoService, + keto service.KetoService, tenantSvc service.TenantService, auth ...interface { GetEnrichedProfile(c *fiber.Ctx) (*domain.UserProfileResponse, error) }, @@ -61,6 +62,7 @@ func NewDevHandler( ConsentRepo: consentRepo, Keto: keto, RPSvc: rpSvc, + TenantSvc: tenantSvc, Auth: authProvider, } } @@ -1746,3 +1748,46 @@ func (h *DevHandler) resolveDevTenantScope(c *fiber.Ctx) string { } return "" } + +// ListMyTenants returns the list of tenants the current user manages or belongs to. +func (h *DevHandler) ListMyTenants(c *fiber.Ctx) error { + profile, err := h.Auth.GetEnrichedProfile(c) + if err != nil || profile == nil { + return errorJSON(c, fiber.StatusUnauthorized, "unauthorized") + } + + role := normalizeUserRole(profile.Role) + if role == domain.RoleUser { + return errorJSON(c, fiber.StatusForbidden, "access denied") + } + + if role == domain.RoleSuperAdmin { + tenants, _, err := h.TenantSvc.ListTenants(c.Context(), 100, 0, "") + if err != nil { + return errorJSON(c, fiber.StatusInternalServerError, "failed to list tenants") + } + return c.JSON(tenants) + } + + tenants, err := h.TenantSvc.ListManageableTenants(c.Context(), profile.ID) + if err != nil { + return errorJSON(c, fiber.StatusInternalServerError, "failed to list manageable tenants: "+err.Error()) + } + + if profile.TenantID != nil && *profile.TenantID != "" { + found := false + for _, t := range tenants { + if t.ID == *profile.TenantID { + found = true + break + } + } + if !found { + if primary, err := h.TenantSvc.GetTenant(c.Context(), *profile.TenantID); err == nil && primary != nil { + tenants = append(tenants, *primary) + } + } + } + + return c.JSON(tenants) +}