forked from baron/baron-sso
Consent 승인 및 해지 이벤트 감사 로그 기록
This commit is contained in:
@@ -3342,6 +3342,24 @@ func (h *AuthHandler) RevokeLinkedRp(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to revoke link")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to revoke link")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.AuditRepo != nil {
|
||||||
|
detailsMap := map[string]interface{}{
|
||||||
|
"client_id": clientID,
|
||||||
|
}
|
||||||
|
detailsBytes, _ := json.Marshal(detailsMap)
|
||||||
|
|
||||||
|
_ = h.AuditRepo.Create(&domain.AuditLog{
|
||||||
|
EventID: GenerateSecureToken(16),
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
UserID: subject,
|
||||||
|
EventType: "consent.revoked",
|
||||||
|
Status: "success",
|
||||||
|
IPAddress: c.IP(),
|
||||||
|
UserAgent: string(c.Request().Header.UserAgent()),
|
||||||
|
Details: string(detailsBytes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"message": "Link revoked successfully",
|
"message": "Link revoked successfully",
|
||||||
@@ -3467,6 +3485,26 @@ func (h *AuthHandler) AcceptConsentRequest(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to accept consent request")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to accept consent request")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.AuditRepo != nil {
|
||||||
|
detailsMap := map[string]interface{}{
|
||||||
|
"client_id": consentRequest.Client.ClientID,
|
||||||
|
"scopes": consentRequest.RequestedScope,
|
||||||
|
"client_name": consentRequest.Client.ClientName,
|
||||||
|
}
|
||||||
|
detailsBytes, _ := json.Marshal(detailsMap)
|
||||||
|
|
||||||
|
_ = h.AuditRepo.Create(&domain.AuditLog{
|
||||||
|
EventID: GenerateSecureToken(16),
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
UserID: consentRequest.Subject,
|
||||||
|
EventType: "consent.granted",
|
||||||
|
Status: "success",
|
||||||
|
IPAddress: c.IP(),
|
||||||
|
UserAgent: string(c.Request().Header.UserAgent()),
|
||||||
|
Details: string(detailsBytes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(acceptResp)
|
return c.JSON(acceptResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4998,3 +5036,100 @@ func mergeScopes(current []string, next []string) []string {
|
|||||||
}
|
}
|
||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type rpHistoryItem struct {
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientName string `json:"client_name"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
LastApprovedAt *time.Time `json:"last_approved_at"`
|
||||||
|
LastRevokedAt *time.Time `json:"last_revoked_at"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) ListRpHistory(c *fiber.Ctx) error {
|
||||||
|
subject, err := h.resolveConsentSubject(c)
|
||||||
|
if err != nil || subject == "" {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid session"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.AuditRepo == nil {
|
||||||
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Audit service unavailable"})
|
||||||
|
}
|
||||||
|
|
||||||
|
logs, err := h.AuditRepo.FindByUserAndEvents(c.Context(), subject, []string{"consent.granted", "consent.revoked"}, 100)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to fetch history"})
|
||||||
|
}
|
||||||
|
|
||||||
|
historyMap := make(map[string]*rpHistoryItem)
|
||||||
|
|
||||||
|
// Logs are DESC (newest first). Iterate in reverse (oldest first) to build state.
|
||||||
|
for i := len(logs) - 1; i >= 0; i-- {
|
||||||
|
log := logs[i]
|
||||||
|
details, _ := parseAuditDetails(log.Details)
|
||||||
|
clientID, _ := details["client_id"].(string)
|
||||||
|
if clientID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
item, ok := historyMap[clientID]
|
||||||
|
if !ok {
|
||||||
|
item = &rpHistoryItem{
|
||||||
|
ClientID: clientID,
|
||||||
|
Status: "unknown",
|
||||||
|
}
|
||||||
|
historyMap[clientID] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, ok := details["client_name"].(string); ok && name != "" {
|
||||||
|
item.ClientName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.EventType == "consent.granted" {
|
||||||
|
item.Status = "active"
|
||||||
|
ts := log.Timestamp
|
||||||
|
item.LastApprovedAt = &ts
|
||||||
|
|
||||||
|
if scopesRaw, ok := details["scopes"].([]interface{}); ok {
|
||||||
|
scopes := make([]string, 0, len(scopesRaw))
|
||||||
|
for _, s := range scopesRaw {
|
||||||
|
if str, ok := s.(string); ok {
|
||||||
|
scopes = append(scopes, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.Scopes = scopes
|
||||||
|
}
|
||||||
|
} else if log.EventType == "consent.revoked" {
|
||||||
|
item.Status = "revoked"
|
||||||
|
ts := log.Timestamp
|
||||||
|
item.LastRevokedAt = &ts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]rpHistoryItem, 0, len(historyMap))
|
||||||
|
for _, item := range historyMap {
|
||||||
|
items = append(items, *item)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(items, func(i, j int) bool {
|
||||||
|
t1 := time.Time{}
|
||||||
|
if items[i].LastApprovedAt != nil {
|
||||||
|
t1 = *items[i].LastApprovedAt
|
||||||
|
}
|
||||||
|
if items[i].LastRevokedAt != nil && items[i].LastRevokedAt.After(t1) {
|
||||||
|
t1 = *items[i].LastRevokedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
t2 := time.Time{}
|
||||||
|
if items[j].LastApprovedAt != nil {
|
||||||
|
t2 = *items[j].LastApprovedAt
|
||||||
|
}
|
||||||
|
if items[j].LastRevokedAt != nil && items[j].LastRevokedAt.After(t2) {
|
||||||
|
t2 = *items[j].LastRevokedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
return t1.After(t2)
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{"items": items})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user