mirror of
https://github.com/teamhanko/hanko.git
synced 2025-10-26 21:57:14 +08:00
116 lines
3.2 KiB
Go
116 lines
3.2 KiB
Go
package auditlog
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/gofrs/uuid"
|
|
"github.com/labstack/echo/v4"
|
|
zeroLog "github.com/rs/zerolog"
|
|
zeroLogger "github.com/rs/zerolog/log"
|
|
"github.com/teamhanko/hanko/backend/config"
|
|
"github.com/teamhanko/hanko/backend/persistence"
|
|
"github.com/teamhanko/hanko/backend/persistence/models"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Logger interface {
|
|
Create(echo.Context, models.AuditLogType, *models.User, error) error
|
|
}
|
|
|
|
type logger struct {
|
|
persister persistence.Persister
|
|
storageEnabled bool
|
|
logger zeroLog.Logger
|
|
consoleLoggingEnabled bool
|
|
}
|
|
|
|
func NewLogger(persister persistence.Persister, cfg config.AuditLog) Logger {
|
|
var loggerOutput *os.File = nil
|
|
switch cfg.ConsoleOutput.OutputStream {
|
|
case config.OutputStreamStdOut:
|
|
loggerOutput = os.Stdout
|
|
case config.OutputStreamStdErr:
|
|
loggerOutput = os.Stderr
|
|
default:
|
|
loggerOutput = os.Stdout
|
|
}
|
|
|
|
return &logger{
|
|
persister: persister,
|
|
storageEnabled: cfg.Storage.Enabled,
|
|
logger: zeroLog.New(loggerOutput),
|
|
consoleLoggingEnabled: cfg.ConsoleOutput.Enabled,
|
|
}
|
|
}
|
|
|
|
func (c *logger) Create(context echo.Context, auditLogType models.AuditLogType, user *models.User, logError error) error {
|
|
if c.storageEnabled {
|
|
err := c.store(context, auditLogType, user, logError)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if c.consoleLoggingEnabled {
|
|
c.logToConsole(context, auditLogType, user, logError)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *logger) store(context echo.Context, auditLogType models.AuditLogType, user *models.User, logError error) error {
|
|
id, err := uuid.NewV4()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create id: %w", err)
|
|
}
|
|
var userId *uuid.UUID = nil
|
|
var userEmail *string = nil
|
|
if user != nil {
|
|
userId = &user.ID
|
|
if e := user.Emails.GetPrimary(); e != nil {
|
|
userEmail = &e.Address
|
|
}
|
|
}
|
|
var errString *string = nil
|
|
if logError != nil {
|
|
// check if error is not nil, because else the string (formatted with fmt.Sprintf) would not be empty but look like this: `%!s(<nil>)`
|
|
tmp := fmt.Sprintf("%s", logError)
|
|
errString = &tmp
|
|
}
|
|
e := models.AuditLog{
|
|
ID: id,
|
|
Type: auditLogType,
|
|
Error: errString,
|
|
MetaHttpRequestId: context.Response().Header().Get(echo.HeaderXRequestID),
|
|
MetaUserAgent: context.Request().UserAgent(),
|
|
MetaSourceIp: context.RealIP(),
|
|
ActorUserId: userId,
|
|
ActorEmail: userEmail,
|
|
}
|
|
|
|
return c.persister.GetAuditLogPersister().Create(e)
|
|
}
|
|
|
|
func (c *logger) logToConsole(context echo.Context, auditLogType models.AuditLogType, user *models.User, logError error) {
|
|
now := time.Now()
|
|
loggerEvent := zeroLogger.Log().
|
|
Str("audience", "audit").
|
|
Str("type", string(auditLogType)).
|
|
AnErr("error", logError).
|
|
Str("http_request_id", context.Response().Header().Get(echo.HeaderXRequestID)).
|
|
Str("source_ip", context.RealIP()).
|
|
Str("user_agent", context.Request().UserAgent()).
|
|
Str("time", now.Format(time.RFC3339Nano)).
|
|
Str("time_unix", strconv.FormatInt(now.Unix(), 10))
|
|
|
|
if user != nil {
|
|
loggerEvent.Str("user_id", user.ID.String())
|
|
if e := user.Emails.GetPrimary(); e != nil {
|
|
loggerEvent.Str("user_email", e.Address)
|
|
}
|
|
}
|
|
|
|
loggerEvent.Send()
|
|
}
|