From be320c98bd86426b52c67a75888c5aa5947a013a Mon Sep 17 00:00:00 2001 From: kyy Date: Thu, 12 Feb 2026 14:18:09 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EA=B3=84?= =?UTF-8?q?=EC=A0=95=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=20=EB=AC=B8=EA=B5=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/bootstrap/kratos_seed.go | 1 + backend/internal/handler/auth_handler.go | 1 + backend/internal/middleware/rbac.go | 19 +++++++++++-------- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/backend/internal/bootstrap/kratos_seed.go b/backend/internal/bootstrap/kratos_seed.go index 7ddb4a4c..b1205cb5 100644 --- a/backend/internal/bootstrap/kratos_seed.go +++ b/backend/internal/bootstrap/kratos_seed.go @@ -34,6 +34,7 @@ func SeedAdminIdentity(idp domain.IdentityProvider) error { "affiliationType": "internal", "companyCode": "", "grade": "admin", + "role": domain.RoleSuperAdmin, }, } diff --git a/backend/internal/handler/auth_handler.go b/backend/internal/handler/auth_handler.go index 1be8ecdb..66ea4ebb 100644 --- a/backend/internal/handler/auth_handler.go +++ b/backend/internal/handler/auth_handler.go @@ -1598,6 +1598,7 @@ func (h *AuthHandler) PasswordLogin(c *fiber.Ctx) error { resp := fiber.Map{ "sessionToken": authInfo.SessionToken.JWT, + "sessionJwt": authInfo.SessionToken.JWT, // Frontend compatibility "status": "ok", "provider": h.IdpProvider.Name(), } diff --git a/backend/internal/middleware/rbac.go b/backend/internal/middleware/rbac.go index 67afe9da..a7f08f5a 100644 --- a/backend/internal/middleware/rbac.go +++ b/backend/internal/middleware/rbac.go @@ -3,6 +3,7 @@ package middleware import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/service" + "fmt" "log/slog" "github.com/gofiber/fiber/v2" @@ -25,7 +26,7 @@ func RequireKetoPermission(config RBACConfig, namespace, relation string) fiber. return func(c *fiber.Ctx) error { profile, err := config.AuthHandler.GetEnrichedProfile(c) if err != nil { - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized (trace:rbac_keto)"}) + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "인증에 실패했습니다. (rbac_keto)"}) } // Store profile in locals for further use in handlers @@ -43,7 +44,7 @@ func RequireKetoPermission(config RBACConfig, namespace, relation string) fiber. } if objectID == "" { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "missing object id for permission check"}) + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "권한 검증을 위한 대상 ID가 누락되었습니다."}) } // Set tenant_id for audit logging if namespace is Tenant @@ -55,7 +56,9 @@ func RequireKetoPermission(config RBACConfig, namespace, relation string) fiber. allowed, err := config.KetoService.CheckPermission(c.Context(), profile.ID, namespace, objectID, relation) if err != nil || !allowed { slog.Warn("Keto permission denied", "userID", profile.ID, "namespace", namespace, "objectID", objectID, "relation", relation) - return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "forbidden: keto permission denied"}) + return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ + "error": fmt.Sprintf("접근 권한이 없습니다. 현재 '%s' 권한으로는 요청하신 리소스에 대한 상세 권한(Keto)이 부족합니다. 관리자에게 문의하세요.", profile.Role), + }) } return c.Next() @@ -73,7 +76,7 @@ func RequireRole(config RBACConfig) fiber.Handler { profile, err := config.AuthHandler.GetEnrichedProfile(c) if err != nil { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "error": "unauthorized (trace:rbac_role): " + err.Error(), + "error": "인증 정보 조회에 실패했습니다: " + err.Error(), }) } @@ -102,7 +105,7 @@ func RequireRole(config RBACConfig) fiber.Handler { "path", c.Path(), ) return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ - "error": "forbidden: insufficient permissions", + "error": fmt.Sprintf("접근 권한이 없습니다. 현재 '%s' 권한으로는 이 기능을 사용할 수 없습니다. 관리자에게 문의하여 'rp_admin' 이상의 권한을 확보하세요.", profile.Role), }) } @@ -123,7 +126,7 @@ func RequireTenantMatch(config RBACConfig) fiber.Handler { profile, err := config.AuthHandler.GetEnrichedProfile(c) if err != nil { - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized (trace:rbac_match)"}) + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "인증에 실패했습니다. (rbac_match)"}) } // Store profile in locals for further use in handlers @@ -143,12 +146,12 @@ func RequireTenantMatch(config RBACConfig) fiber.Handler { if profile.TenantID == nil || *profile.TenantID != targetTenantID { return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ - "error": "forbidden: you do not have access to this tenant", + "error": fmt.Sprintf("해당 테넌트에 대한 접근 권한이 없습니다. 사용자님의 '%s' 권한은 소속된 테넌트의 리소스만 관리할 수 있습니다.", profile.Role), }) } return c.Next() } - return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "forbidden"}) + return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "요청하신 리소스에 접근할 수 없습니다."}) } }