mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-08 18:59:10 +02:00
101 lines
3.8 KiB
Go
101 lines
3.8 KiB
Go
// Package auditlogs owns audit records, their HTTP API, sign-in notifications, and retention cleanup
|
|
package auditlogs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
francishost "github.com/italypaleale/francis/host"
|
|
"github.com/lestrrat-go/jwx/v4/jwt"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/pocket-id/pocket-id/backend/internal/appconfig"
|
|
"github.com/pocket-id/pocket-id/backend/internal/httpserver"
|
|
"github.com/pocket-id/pocket-id/backend/internal/iplocation"
|
|
)
|
|
|
|
type NewLoginEmailSender interface {
|
|
SendNewLogin(ctx context.Context, dbConfig *appconfig.AppConfigModel, userFullName, userEmail, ipAddress, country, city, device, method string, dateTime time.Time) error
|
|
}
|
|
|
|
type SessionTokenService interface {
|
|
SignSessionToken(token jwt.Token) (string, error)
|
|
VerifySessionToken(token string, options ...jwt.ValidateOption) (jwt.Token, error)
|
|
}
|
|
|
|
type Dependencies struct {
|
|
DB *gorm.DB
|
|
Actors francishost.Host
|
|
|
|
EmailSender NewLoginEmailSender
|
|
IPLocator iplocation.Resolver
|
|
AppConfig appconfig.AppConfigResolver
|
|
Signer SessionTokenService
|
|
AppURL string
|
|
|
|
// RetentionDays is how long audit logs are kept before the cleanup job deletes them
|
|
RetentionDays int
|
|
|
|
// CleanupDisabled skips registering cleanup cron jobs, for example in tests
|
|
CleanupDisabled bool
|
|
}
|
|
|
|
type Module struct {
|
|
service *service
|
|
handler *handler
|
|
}
|
|
|
|
func New(deps Dependencies) (*Module, error) {
|
|
// Register audit retention cleanup before the actor host starts
|
|
if !deps.CleanupDisabled {
|
|
if deps.Actors == nil {
|
|
return nil, errors.New("actor host is required for the audit log cleanup cron job")
|
|
}
|
|
|
|
jobs, err := newCleanupJobs(deps.DB, deps.RetentionDays)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, cj := range jobs {
|
|
if err := deps.Actors.RegisterBuiltInActor(cj); err != nil {
|
|
return nil, fmt.Errorf("error registering audit log cleanup cron actor %q: %w", cj.ActorType(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
service := newService(deps.DB, deps.EmailSender, deps.IPLocator, deps.AppConfig, &browserTokens{signer: deps.Signer, appURL: deps.AppURL})
|
|
return &Module{service: service, handler: newHandler(service)}, nil
|
|
}
|
|
|
|
// RegisterRoutes mounts audit-log queries with the existing admin and current-user permissions
|
|
func (m *Module) RegisterRoutes(group *gin.RouterGroup, adminAuth, userAuth gin.HandlerFunc) {
|
|
group.GET("/audit-logs/all", adminAuth, httpserver.Handle(m.handler.listAllAuditLogsHandler))
|
|
group.GET("/audit-logs", userAuth, httpserver.Handle(m.handler.listAuditLogsForUserHandler))
|
|
group.GET("/audit-logs/filters/client-names", adminAuth, httpserver.Handle(m.handler.listClientNamesHandler))
|
|
group.GET("/audit-logs/filters/users", adminAuth, httpserver.Handle(m.handler.listUserNamesWithIdsHandler))
|
|
}
|
|
|
|
// Create records an event within the caller's transaction
|
|
func (m *Module) Create(ctx context.Context, event Event, ipAddress, userAgent, userID string, data Data, tx *gorm.DB) (AuditLog, bool) {
|
|
return m.service.Create(ctx, event, ipAddress, userAgent, userID, data, tx)
|
|
}
|
|
|
|
// CreateSignIn prepares browser recognition and notification delivery within the caller's transaction
|
|
func (m *Module) CreateSignIn(ctx context.Context, event Event, ipAddress, userAgent, userID, browserToken string, tx *gorm.DB, notificationMode appconfig.AppConfigValue) SignInResult {
|
|
return m.service.CreateSignIn(ctx, event, ipAddress, userAgent, userID, browserToken, tx, notificationMode)
|
|
}
|
|
|
|
// SendSignInNotification must be called only after the login commits successfully
|
|
func (m *Module) SendSignInNotification(ctx context.Context, result SignInResult) {
|
|
m.service.SendSignInNotification(ctx, result)
|
|
}
|
|
|
|
// DeviceStringFromUserAgent describes a browser for audit records and device-login approval
|
|
func (m *Module) DeviceStringFromUserAgent(userAgent string) string {
|
|
return m.service.DeviceStringFromUserAgent(userAgent)
|
|
}
|