1
0
forked from baron/baron-sso

DevHandler 동의 내역 조회 API를 로컬 DB 기반으로 변경

This commit is contained in:
2026-02-06 11:23:52 +09:00
parent 94c2ce9f31
commit 7a057fa5db

View File

@@ -2,6 +2,7 @@ package handler
import (
"baron-sso-backend/internal/domain"
"baron-sso-backend/internal/repository"
"baron-sso-backend/internal/service"
"context"
"errors"
@@ -17,14 +18,16 @@ type DevHandler struct {
Redis *service.RedisService
SecretRepo domain.ClientSecretRepository
KratosAdmin *service.KratosAdminService
ConsentRepo repository.ClientConsentRepository
}
func NewDevHandler(redis *service.RedisService, secretRepo domain.ClientSecretRepository) *DevHandler {
func NewDevHandler(redis *service.RedisService, secretRepo domain.ClientSecretRepository, consentRepo repository.ClientConsentRepository) *DevHandler {
return &DevHandler{
Hydra: service.NewHydraAdminService(),
Redis: redis,
SecretRepo: secretRepo,
KratosAdmin: service.NewKratosAdminService(),
ConsentRepo: consentRepo,
}
}
@@ -60,12 +63,15 @@ type clientEndpoints struct {
}
type consentSummary struct {
Subject string `json:"subject"`
UserName string `json:"userName,omitempty"`
ClientID string `json:"clientId"`
ClientName string `json:"clientName,omitempty"`
GrantedScopes []string `json:"grantedScopes"`
AuthenticatedAt string `json:"authenticatedAt,omitempty"`
Subject string `json:"subject"`
UserName string `json:"userName,omitempty"`
ClientID string `json:"clientId"`
ClientName string `json:"clientName,omitempty"`
GrantedScopes []string `json:"grantedScopes"`
AuthenticatedAt string `json:"authenticatedAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
TenantID string `json:"tenantId,omitempty"`
TenantName string `json:"tenantName,omitempty"`
}
type consentListResponse struct {
@@ -394,70 +400,84 @@ func (h *DevHandler) DeleteClient(c *fiber.Ctx) error {
}
func (h *DevHandler) ListConsents(c *fiber.Ctx) error {
subject := strings.TrimSpace(c.Query("subject"))
if subject == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "subject is required"})
}
// If subject is not a UUID, try to resolve it as an identifier (email/username)
if _, err := uuid.Parse(subject); err != nil {
resolved, err := h.KratosAdmin.FindIdentityIDByIdentifier(c.Context(), subject)
if err == nil && resolved != "" {
subject = resolved
}
}
clientID := strings.TrimSpace(c.Query("client_id"))
if clientID == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "client_id is required"})
}
subject := strings.TrimSpace(c.Query("subject"))
limit := c.QueryInt("limit", 50)
offset := c.QueryInt("offset", 0)
if limit <= 0 {
limit = 50
}
// [Isolation] Get admin tenant ID from header or locals
adminTenantID := c.Get("X-Tenant-ID") // Assume middleware sets this or trusted in dev
var consents []domain.ClientConsentWithTenantInfo
var total int64
var err error
if subject != "" {
// Resolve subject if it's email/name (Legacy support)
if _, err := uuid.Parse(subject); err != nil {
resolved, _ := h.KratosAdmin.FindIdentityIDByIdentifier(c.Context(), subject)
if resolved != "" {
subject = resolved
}
}
// Single user fetch from Hydra (to get latest status) or Local DB
// Issue says: "List All", so we prefer Local DB for consistency in listing
// But for a single user, we could still use Hydra.
// Let's use Local DB to support tenant filtering even for search.
// For simplicity, we just filter the list later if search is used.
}
if adminTenantID != "" {
consents, total, err = h.ConsentRepo.ListByTenant(c.Context(), clientID, adminTenantID, limit, offset)
} else {
consents, total, err = h.ConsentRepo.List(c.Context(), clientID, limit, offset)
}
sessions, err := h.Hydra.ListConsentSessions(c.Context(), subject, clientID)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
items := make([]consentSummary, 0, len(sessions))
for _, session := range sessions {
client := session.Client
if client.ClientID == "" && session.ConsentRequest != nil {
client = session.ConsentRequest.Client
}
subject := session.Subject
if subject == "" && session.ConsentRequest != nil {
subject = session.ConsentRequest.Subject
items := make([]consentSummary, 0, len(consents))
for _, consent := range consents {
// Filter by subject if search is active
if subject != "" && consent.Subject != subject {
continue
}
userName := ""
if subject != "" {
identity, err := h.KratosAdmin.GetIdentity(c.Context(), subject)
if err == nil && identity != nil {
if name, ok := identity.Traits["name"].(string); ok {
userName = name
} else if displayName, ok := identity.Traits["displayname"].(string); ok {
userName = displayName
} else if email, ok := identity.Traits["email"].(string); ok {
userName = email
}
identity, err := h.KratosAdmin.GetIdentity(c.Context(), consent.Subject)
if err == nil && identity != nil {
if name, ok := identity.Traits["name"].(string); ok {
userName = name
} else if email, ok := identity.Traits["email"].(string); ok {
userName = email
}
}
authAt := ""
if session.AuthenticatedAt != nil {
authAt = session.AuthenticatedAt.Format(time.RFC3339)
} else if session.RequestedAt != nil {
authAt = session.RequestedAt.Format(time.RFC3339)
} else if session.HandledAt != nil {
authAt = session.HandledAt.Format(time.RFC3339)
}
items = append(items, consentSummary{
Subject: subject,
Subject: consent.Subject,
UserName: userName,
ClientID: client.ClientID,
ClientName: client.ClientName,
GrantedScopes: session.GrantedScope,
AuthenticatedAt: authAt,
ClientID: consent.ClientID,
GrantedScopes: consent.GrantedScopes,
AuthenticatedAt: consent.UpdatedAt.Format(time.RFC3339),
CreatedAt: consent.CreatedAt,
TenantID: consent.TenantID,
TenantName: consent.TenantName,
})
}
return c.JSON(consentListResponse{Items: items})
return c.JSON(fiber.Map{
"items": items,
"total": total,
})
}
func (h *DevHandler) RevokeConsents(c *fiber.Ctx) error {
@@ -465,6 +485,7 @@ func (h *DevHandler) RevokeConsents(c *fiber.Ctx) error {
if subject == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "subject is required"})
}
clientID := strings.TrimSpace(c.Query("client_id"))
// If subject is not a UUID, try to resolve it as an identifier (email/username)
if _, err := uuid.Parse(subject); err != nil {
@@ -474,12 +495,16 @@ func (h *DevHandler) RevokeConsents(c *fiber.Ctx) error {
}
}
clientID := strings.TrimSpace(c.Query("client_id"))
// 1. Revoke in Hydra
if err := h.Hydra.RevokeConsentSessions(c.Context(), subject, clientID); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
// 2. Sync to Local DB (Delete)
if h.ConsentRepo != nil {
_ = h.ConsentRepo.Delete(c.Context(), subject, clientID)
}
return c.SendStatus(fiber.StatusNoContent)
}