rbac system first stage
This commit is contained in:
166
internal/auditlog/logger.go
Normal file
166
internal/auditlog/logger.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Package auditlog provides a system for logging events that require permissions to the audit log
|
||||
package auditlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
conn *bun.DB
|
||||
}
|
||||
|
||||
func NewLogger(conn *bun.DB) *Logger {
|
||||
return &Logger{conn: conn}
|
||||
}
|
||||
|
||||
// LogSuccess logs a successful permission-protected action
|
||||
func (l *Logger) LogSuccess(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
user *db.User,
|
||||
action string,
|
||||
resourceType string,
|
||||
resourceID any, // Can be int, string, or nil
|
||||
details map[string]any,
|
||||
r *http.Request,
|
||||
) error {
|
||||
return l.log(ctx, tx, user, action, resourceType, resourceID, details, "success", nil, r)
|
||||
}
|
||||
|
||||
// LogError logs a failed action due to an error
|
||||
func (l *Logger) LogError(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
user *db.User,
|
||||
action string,
|
||||
resourceType string,
|
||||
resourceID any,
|
||||
err error,
|
||||
r *http.Request,
|
||||
) error {
|
||||
errMsg := err.Error()
|
||||
return l.log(ctx, tx, user, action, resourceType, resourceID, nil, "error", &errMsg, r)
|
||||
}
|
||||
|
||||
func (l *Logger) log(
|
||||
ctx context.Context,
|
||||
tx bun.Tx,
|
||||
user *db.User,
|
||||
action string,
|
||||
resourceType string,
|
||||
resourceID any,
|
||||
details map[string]any,
|
||||
result string,
|
||||
errorMessage *string,
|
||||
r *http.Request,
|
||||
) error {
|
||||
if user == nil {
|
||||
return errors.New("user cannot be nil for audit logging")
|
||||
}
|
||||
|
||||
// Convert resourceID to string
|
||||
var resourceIDStr *string
|
||||
if resourceID != nil {
|
||||
idStr := fmt.Sprintf("%v", resourceID)
|
||||
resourceIDStr = &idStr
|
||||
}
|
||||
|
||||
// Marshal details to JSON
|
||||
var detailsJSON json.RawMessage
|
||||
if details != nil {
|
||||
jsonBytes, err := json.Marshal(details)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "json.Marshal details")
|
||||
}
|
||||
detailsJSON = jsonBytes
|
||||
}
|
||||
|
||||
// Extract IP and User-Agent from request
|
||||
ipAddress := r.RemoteAddr
|
||||
userAgent := r.UserAgent()
|
||||
|
||||
log := &db.AuditLog{
|
||||
UserID: user.ID,
|
||||
Action: action,
|
||||
ResourceType: resourceType,
|
||||
ResourceID: resourceIDStr,
|
||||
Details: detailsJSON,
|
||||
IPAddress: ipAddress,
|
||||
UserAgent: userAgent,
|
||||
Result: result,
|
||||
ErrorMessage: errorMessage,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}
|
||||
|
||||
return db.CreateAuditLog(ctx, tx, log)
|
||||
}
|
||||
|
||||
// GetRecentLogs retrieves recent audit logs with pagination
|
||||
// TODO: change this to user db.PageOpts
|
||||
func (l *Logger) GetRecentLogs(ctx context.Context, limit, offset int) ([]*db.AuditLog, int, error) {
|
||||
tx, err := l.conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "conn.BeginTx")
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
logs, total, err := db.GetAuditLogs(ctx, tx, limit, offset, nil)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
_ = tx.Commit() // read only transaction
|
||||
return logs, total, nil
|
||||
}
|
||||
|
||||
// GetLogsByUser retrieves audit logs for a specific user
|
||||
// TODO: change this to user db.PageOpts
|
||||
func (l *Logger) GetLogsByUser(ctx context.Context, userID int, limit, offset int) ([]*db.AuditLog, int, error) {
|
||||
tx, err := l.conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, "conn.BeginTx")
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
logs, total, err := db.GetAuditLogsByUser(ctx, tx, userID, limit, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
_ = tx.Commit() // read only transaction
|
||||
return logs, total, nil
|
||||
}
|
||||
|
||||
// CleanupOldLogs deletes audit logs older than the specified number of days
|
||||
func (l *Logger) CleanupOldLogs(ctx context.Context, daysToKeep int) (int, error) {
|
||||
if daysToKeep <= 0 {
|
||||
return 0, errors.New("daysToKeep must be positive")
|
||||
}
|
||||
|
||||
cutoffTime := time.Now().AddDate(0, 0, -daysToKeep).Unix()
|
||||
|
||||
tx, err := l.conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "conn.BeginTx")
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
count, err := db.CleanupOldAuditLogs(ctx, tx, cutoffTime)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "tx.Commit")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
Reference in New Issue
Block a user