forked from baron/baron-sso
DevHandler 동의 내역 조회 API를 로컬 DB 기반으로 변경
This commit is contained in:
@@ -2,6 +2,7 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"baron-sso-backend/internal/domain"
|
"baron-sso-backend/internal/domain"
|
||||||
|
"baron-sso-backend/internal/repository"
|
||||||
"baron-sso-backend/internal/service"
|
"baron-sso-backend/internal/service"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -17,14 +18,16 @@ type DevHandler struct {
|
|||||||
Redis *service.RedisService
|
Redis *service.RedisService
|
||||||
SecretRepo domain.ClientSecretRepository
|
SecretRepo domain.ClientSecretRepository
|
||||||
KratosAdmin *service.KratosAdminService
|
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{
|
return &DevHandler{
|
||||||
Hydra: service.NewHydraAdminService(),
|
Hydra: service.NewHydraAdminService(),
|
||||||
Redis: redis,
|
Redis: redis,
|
||||||
SecretRepo: secretRepo,
|
SecretRepo: secretRepo,
|
||||||
KratosAdmin: service.NewKratosAdminService(),
|
KratosAdmin: service.NewKratosAdminService(),
|
||||||
|
ConsentRepo: consentRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,12 +63,15 @@ type clientEndpoints struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type consentSummary struct {
|
type consentSummary struct {
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
UserName string `json:"userName,omitempty"`
|
UserName string `json:"userName,omitempty"`
|
||||||
ClientID string `json:"clientId"`
|
ClientID string `json:"clientId"`
|
||||||
ClientName string `json:"clientName,omitempty"`
|
ClientName string `json:"clientName,omitempty"`
|
||||||
GrantedScopes []string `json:"grantedScopes"`
|
GrantedScopes []string `json:"grantedScopes"`
|
||||||
AuthenticatedAt string `json:"authenticatedAt,omitempty"`
|
AuthenticatedAt string `json:"authenticatedAt,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
TenantID string `json:"tenantId,omitempty"`
|
||||||
|
TenantName string `json:"tenantName,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type consentListResponse struct {
|
type consentListResponse struct {
|
||||||
@@ -394,70 +400,84 @@ func (h *DevHandler) DeleteClient(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *DevHandler) ListConsents(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"))
|
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 {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
items := make([]consentSummary, 0, len(sessions))
|
items := make([]consentSummary, 0, len(consents))
|
||||||
for _, session := range sessions {
|
for _, consent := range consents {
|
||||||
client := session.Client
|
// Filter by subject if search is active
|
||||||
if client.ClientID == "" && session.ConsentRequest != nil {
|
if subject != "" && consent.Subject != subject {
|
||||||
client = session.ConsentRequest.Client
|
continue
|
||||||
}
|
|
||||||
subject := session.Subject
|
|
||||||
if subject == "" && session.ConsentRequest != nil {
|
|
||||||
subject = session.ConsentRequest.Subject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userName := ""
|
userName := ""
|
||||||
if subject != "" {
|
identity, err := h.KratosAdmin.GetIdentity(c.Context(), consent.Subject)
|
||||||
identity, err := h.KratosAdmin.GetIdentity(c.Context(), subject)
|
if err == nil && identity != nil {
|
||||||
if err == nil && identity != nil {
|
if name, ok := identity.Traits["name"].(string); ok {
|
||||||
if name, ok := identity.Traits["name"].(string); ok {
|
userName = name
|
||||||
userName = name
|
} else if email, ok := identity.Traits["email"].(string); ok {
|
||||||
} else if displayName, ok := identity.Traits["displayname"].(string); ok {
|
userName = email
|
||||||
userName = displayName
|
|
||||||
} 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{
|
items = append(items, consentSummary{
|
||||||
Subject: subject,
|
Subject: consent.Subject,
|
||||||
UserName: userName,
|
UserName: userName,
|
||||||
ClientID: client.ClientID,
|
ClientID: consent.ClientID,
|
||||||
ClientName: client.ClientName,
|
GrantedScopes: consent.GrantedScopes,
|
||||||
GrantedScopes: session.GrantedScope,
|
AuthenticatedAt: consent.UpdatedAt.Format(time.RFC3339),
|
||||||
AuthenticatedAt: authAt,
|
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 {
|
func (h *DevHandler) RevokeConsents(c *fiber.Ctx) error {
|
||||||
@@ -465,6 +485,7 @@ func (h *DevHandler) RevokeConsents(c *fiber.Ctx) error {
|
|||||||
if subject == "" {
|
if subject == "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "subject is required"})
|
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 subject is not a UUID, try to resolve it as an identifier (email/username)
|
||||||
if _, err := uuid.Parse(subject); err != nil {
|
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 {
|
if err := h.Hydra.RevokeConsentSessions(c.Context(), subject, clientID); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
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)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user