audit.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. package logger
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "time"
  6. _ "modernc.org/sqlite"
  7. )
  8. // auditConfig 审计配置
  9. type auditConfig struct {
  10. enabled bool
  11. dbPath string
  12. retentionDays int
  13. bufferSize int
  14. }
  15. // AuditOption 审计配置选项
  16. type AuditOption func(*auditConfig)
  17. // WithDatabasePath 设置数据库路径
  18. func WithDatabasePath(path string) AuditOption {
  19. return func(c *auditConfig) {
  20. c.dbPath = path
  21. }
  22. }
  23. // WithRetention 设置保留天数
  24. func WithRetention(days int) AuditOption {
  25. return func(c *auditConfig) {
  26. c.retentionDays = days
  27. }
  28. }
  29. // WithBufferSize 设置缓冲区大小
  30. func WithBufferSize(size int) AuditOption {
  31. return func(c *auditConfig) {
  32. c.bufferSize = size
  33. }
  34. }
  35. // WithEnabled 设置是否启用审计
  36. func WithEnabled(enabled bool) AuditOption {
  37. return func(c *auditConfig) {
  38. c.enabled = enabled
  39. }
  40. }
  41. // sqliteAuditLogger SQLite审计日志器实现
  42. type sqliteAuditLogger struct {
  43. db *sql.DB
  44. config *auditConfig
  45. }
  46. // NewAuditLogger 创建审计日志器
  47. func NewAuditLogger(opts ...AuditOption) (AuditLogger, error) {
  48. config := &auditConfig{
  49. enabled: true,
  50. dbPath: "audit.db",
  51. retentionDays: 90,
  52. bufferSize: 1000,
  53. }
  54. for _, opt := range opts {
  55. opt(config)
  56. }
  57. if !config.enabled {
  58. return &noOpAuditLogger{}, nil
  59. }
  60. db, err := sql.Open("sqlite", config.dbPath)
  61. if err != nil {
  62. return nil, fmt.Errorf("failed to open database: %w", err)
  63. }
  64. if err := initAuditTable(db); err != nil {
  65. db.Close()
  66. return nil, fmt.Errorf("failed to initialize audit table: %w", err)
  67. }
  68. return &sqliteAuditLogger{
  69. db: db,
  70. config: config,
  71. }, nil
  72. }
  73. // Record 记录审计事件
  74. func (a *sqliteAuditLogger) Record(entry *AuditEntry) error {
  75. if entry == nil {
  76. return fmt.Errorf("audit entry cannot be nil")
  77. }
  78. if entry.ID == "" {
  79. entry.ID = fmt.Sprintf("%d", time.Now().UnixNano())
  80. }
  81. if entry.Timestamp.IsZero() {
  82. entry.Timestamp = time.Now()
  83. }
  84. query := `
  85. INSERT INTO audit_logs (id, timestamp, user_id, username, action, resource, details, ip, user_agent, success, error)
  86. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  87. `
  88. _, err := a.db.Exec(query,
  89. entry.ID,
  90. entry.Timestamp,
  91. entry.UserID,
  92. entry.Username,
  93. entry.Action,
  94. entry.Resource,
  95. entry.Details,
  96. entry.IP,
  97. entry.UserAgent,
  98. entry.Success,
  99. entry.Error,
  100. )
  101. if err != nil {
  102. return fmt.Errorf("failed to record audit entry: %w", err)
  103. }
  104. return nil
  105. }
  106. // Query 查询审计日志
  107. func (a *sqliteAuditLogger) Query(filter *AuditFilter) ([]*AuditEntry, error) {
  108. if filter == nil {
  109. filter = &AuditFilter{}
  110. }
  111. query := `SELECT id, timestamp, user_id, username, action, resource, details, ip, user_agent, success, error FROM audit_logs WHERE 1=1`
  112. args := []interface{}{}
  113. if filter.UserID != "" {
  114. query += " AND user_id = ?"
  115. args = append(args, filter.UserID)
  116. }
  117. if filter.Action != "" {
  118. query += " AND action = ?"
  119. args = append(args, filter.Action)
  120. }
  121. if filter.Resource != "" {
  122. query += " AND resource = ?"
  123. args = append(args, filter.Resource)
  124. }
  125. if filter.StartTime != nil {
  126. query += " AND timestamp >= ?"
  127. args = append(args, *filter.StartTime)
  128. }
  129. if filter.EndTime != nil {
  130. query += " AND timestamp <= ?"
  131. args = append(args, *filter.EndTime)
  132. }
  133. if filter.Success != nil {
  134. query += " AND success = ?"
  135. args = append(args, *filter.Success)
  136. }
  137. query += " ORDER BY timestamp DESC"
  138. if filter.Limit > 0 {
  139. query += " LIMIT ?"
  140. args = append(args, filter.Limit)
  141. }
  142. if filter.Offset > 0 {
  143. query += " OFFSET ?"
  144. args = append(args, filter.Offset)
  145. }
  146. rows, err := a.db.Query(query, args...)
  147. if err != nil {
  148. return nil, fmt.Errorf("failed to query audit logs: %w", err)
  149. }
  150. defer rows.Close()
  151. var entries []*AuditEntry
  152. for rows.Next() {
  153. entry := &AuditEntry{}
  154. err := rows.Scan(
  155. &entry.ID,
  156. &entry.Timestamp,
  157. &entry.UserID,
  158. &entry.Username,
  159. &entry.Action,
  160. &entry.Resource,
  161. &entry.Details,
  162. &entry.IP,
  163. &entry.UserAgent,
  164. &entry.Success,
  165. &entry.Error,
  166. )
  167. if err != nil {
  168. return nil, fmt.Errorf("failed to scan audit entry: %w", err)
  169. }
  170. entries = append(entries, entry)
  171. }
  172. return entries, nil
  173. }
  174. // Cleanup 清理过期数据
  175. func (a *sqliteAuditLogger) Cleanup(retention time.Duration) error {
  176. cutoff := time.Now().Add(-retention)
  177. query := "DELETE FROM audit_logs WHERE timestamp < ?"
  178. _, err := a.db.Exec(query, cutoff)
  179. if err != nil {
  180. return fmt.Errorf("failed to cleanup audit logs: %w", err)
  181. }
  182. // 优化数据库
  183. _, err = a.db.Exec("VACUUM")
  184. if err != nil {
  185. return fmt.Errorf("failed to vacuum database: %w", err)
  186. }
  187. return nil
  188. }
  189. // Sync 同步数据到磁盘
  190. func (a *sqliteAuditLogger) Sync() error {
  191. _, err := a.db.Exec("PRAGMA wal_checkpoint(FULL)")
  192. return err
  193. }
  194. // initAuditTable 初始化审计表
  195. func initAuditTable(db *sql.DB) error {
  196. query := `
  197. CREATE TABLE IF NOT EXISTS audit_logs (
  198. id TEXT PRIMARY KEY,
  199. timestamp DATETIME NOT NULL,
  200. user_id TEXT,
  201. username TEXT,
  202. action TEXT NOT NULL,
  203. resource TEXT,
  204. details TEXT,
  205. ip TEXT,
  206. user_agent TEXT,
  207. success BOOLEAN,
  208. error TEXT
  209. );
  210. CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_logs(timestamp);
  211. CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_logs(user_id);
  212. CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_logs(action);
  213. CREATE INDEX IF NOT EXISTS idx_audit_resource ON audit_logs(resource);
  214. `
  215. _, err := db.Exec(query)
  216. return err
  217. }
  218. // noOpAuditLogger 空操作审计日志器(当审计被禁用时使用)
  219. type noOpAuditLogger struct{}
  220. func (n *noOpAuditLogger) Record(entry *AuditEntry) error {
  221. return nil
  222. }
  223. func (n *noOpAuditLogger) Query(filter *AuditFilter) ([]*AuditEntry, error) {
  224. return []*AuditEntry{}, nil
  225. }
  226. func (n *noOpAuditLogger) Cleanup(retention time.Duration) error {
  227. return nil
  228. }
  229. func (n *noOpAuditLogger) Sync() error {
  230. return nil
  231. }