NET-2014: add audit log retention period, add timestamp for events (#3486)

* revert inet gws from acl policies

* add egress range with metric for inet gw

* link pro inet funcs

* add timestamp params to activity apis
This commit is contained in:
Abhishek K 2025-06-06 13:19:56 +05:30 committed by GitHub
parent adc4d7f3dd
commit 810ff21165
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 164 additions and 49 deletions

View file

@ -578,4 +578,9 @@ func settings() {
if database.IsEmptyRecord(err) {
logic.UpsertServerSettings(logic.GetServerSettingsFromEnv())
}
settings := logic.GetServerSettings()
if settings.AuditLogsRetentionPeriodInDays == 0 {
settings.AuditLogsRetentionPeriodInDays = 30
}
logic.UpsertServerSettings(settings)
}

View file

@ -43,4 +43,5 @@ type ServerSettings struct {
Theme Theme `json:"theme"`
TextSize string `json:"text_size"`
ReducedMotion bool `json:"reduced_motion"`
AuditLogsRetentionPeriodInDays int `json:"audit_logs_retention_period"`
}

View file

@ -3,6 +3,7 @@ package controllers
import (
"net/http"
"strconv"
"time"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/db"
@ -33,10 +34,33 @@ func listNetworkActivity(w http.ResponseWriter, r *http.Request) {
})
return
}
fromDateStr := r.URL.Query().Get("from_date")
toDateStr := r.URL.Query().Get("to_date")
var err error
var fromDate, toDate time.Time
if fromDateStr != "" && toDateStr != "" {
fromDate, err = time.Parse(time.RFC3339, fromDateStr)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusBadRequest,
Message: err.Error(),
})
return
}
toDate, err = time.Parse(time.RFC3339, toDateStr)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusBadRequest,
Message: err.Error(),
})
return
}
}
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
pageSize, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
ctx := db.WithContext(r.Context())
netActivity, err := (&schema.Event{NetworkID: models.NetworkID(netID)}).ListByNetwork(db.SetPagination(ctx, page, pageSize))
netActivity, err := (&schema.Event{NetworkID: models.NetworkID(netID)}).ListByNetwork(db.SetPagination(ctx, page, pageSize), fromDate, toDate)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusInternalServerError,
@ -64,10 +88,32 @@ func listUserActivity(w http.ResponseWriter, r *http.Request) {
})
return
}
fromDateStr := r.URL.Query().Get("from_date")
toDateStr := r.URL.Query().Get("to_date")
var err error
var fromDate, toDate time.Time
if fromDateStr != "" && toDateStr != "" {
fromDate, err = time.Parse(time.RFC3339, fromDateStr)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusBadRequest,
Message: err.Error(),
})
return
}
toDate, err = time.Parse(time.RFC3339, toDateStr)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusBadRequest,
Message: err.Error(),
})
return
}
}
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
pageSize, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
ctx := db.WithContext(r.Context())
userActivity, err := (&schema.Event{TriggeredBy: username}).ListByUser(db.SetPagination(ctx, page, pageSize))
userActivity, err := (&schema.Event{TriggeredBy: username}).ListByUser(db.SetPagination(ctx, page, pageSize), fromDate, toDate)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusInternalServerError,
@ -91,16 +137,37 @@ func listActivity(w http.ResponseWriter, r *http.Request) {
pageSize, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
ctx := db.WithContext(r.Context())
var err error
fromDateStr := r.URL.Query().Get("from_date")
toDateStr := r.URL.Query().Get("to_date")
var fromDate, toDate time.Time
if fromDateStr != "" && toDateStr != "" {
fromDate, err = time.Parse(time.RFC3339, fromDateStr)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusBadRequest,
Message: err.Error(),
})
return
}
toDate, err = time.Parse(time.RFC3339, toDateStr)
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
Code: http.StatusBadRequest,
Message: err.Error(),
})
return
}
}
var events []schema.Event
e := &schema.Event{TriggeredBy: username, NetworkID: models.NetworkID(network)}
if username != "" && network != "" {
events, err = e.ListByUserAndNetwork(db.SetPagination(ctx, page, pageSize))
events, err = e.ListByUserAndNetwork(db.SetPagination(ctx, page, pageSize), fromDate, toDate)
} else if username != "" && network == "" {
events, err = e.ListByUser(db.SetPagination(ctx, page, pageSize))
events, err = e.ListByUser(db.SetPagination(ctx, page, pageSize), fromDate, toDate)
} else if username == "" && network != "" {
events, err = e.ListByNetwork(db.SetPagination(ctx, page, pageSize))
events, err = e.ListByNetwork(db.SetPagination(ctx, page, pageSize), fromDate, toDate)
} else {
events, err = e.List(db.SetPagination(ctx, page, pageSize))
events, err = e.List(db.SetPagination(ctx, page, pageSize), fromDate, toDate)
}
if err != nil {
logic.ReturnErrorResponse(w, r, models.ErrorResponse{

View file

@ -3,11 +3,13 @@ package logic
import (
"context"
"encoding/json"
"log/slog"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/schema"
)
@ -18,8 +20,25 @@ func LogEvent(a *models.Event) {
EventActivityCh <- *a
}
func EventWatcher() {
func EventRententionHook() error {
settings := logic.GetServerSettings()
retentionPeriod := settings.AuditLogsRetentionPeriodInDays
if retentionPeriod <= 0 {
retentionPeriod = 30
}
err := (&schema.Event{}).DeleteOldEvents(db.WithContext(context.TODO()), retentionPeriod)
if err != nil {
slog.Warn("failed to delete old events pas retention period", "error", err)
}
return nil
}
func EventWatcher() {
logic.HookManagerCh <- models.HookDetails{
Hook: EventRententionHook,
Interval: time.Hour * 24,
}
for e := range EventActivityCh {
if e.Action == models.Update {
// check if diff

View file

@ -1,4 +0,0 @@
package schema
type Activity struct {
}

View file

@ -33,23 +33,49 @@ func (a *Event) Create(ctx context.Context) error {
return db.FromContext(ctx).Model(&Event{}).Create(&a).Error
}
func (a *Event) ListByNetwork(ctx context.Context) (ats []Event, err error) {
func (a *Event) ListByNetwork(ctx context.Context, from, to time.Time) (ats []Event, err error) {
if !from.IsZero() && !to.IsZero() {
// "created_at BETWEEN ? AND ?
err = db.FromContext(ctx).Model(&Event{}).Where("network_id = ? AND time_stamp BETWEEN ? AND ?",
a.NetworkID, from, to).Order("time_stamp DESC").Find(&ats).Error
return
}
err = db.FromContext(ctx).Model(&Event{}).Where("network_id = ?", a.NetworkID).Order("time_stamp DESC").Find(&ats).Error
return
}
func (a *Event) ListByUser(ctx context.Context) (ats []Event, err error) {
func (a *Event) ListByUser(ctx context.Context, from, to time.Time) (ats []Event, err error) {
if !from.IsZero() && !to.IsZero() {
err = db.FromContext(ctx).Model(&Event{}).Where("triggered_by = ? AND time_stamp BETWEEN ? AND ?",
a.TriggeredBy, from, to).Order("time_stamp DESC").Find(&ats).Error
return
}
err = db.FromContext(ctx).Model(&Event{}).Where("triggered_by = ?", a.TriggeredBy).Order("time_stamp DESC").Find(&ats).Error
return
}
func (a *Event) ListByUserAndNetwork(ctx context.Context) (ats []Event, err error) {
func (a *Event) ListByUserAndNetwork(ctx context.Context, from, to time.Time) (ats []Event, err error) {
if !from.IsZero() && !to.IsZero() {
err = db.FromContext(ctx).Model(&Event{}).Where("network_id = ? AND triggered_by = ? AND time_stamp BETWEEN ? AND ?",
a.NetworkID, a.TriggeredBy, from, to).Order("time_stamp DESC").Find(&ats).Error
return
}
err = db.FromContext(ctx).Model(&Event{}).Where("network_id = ? AND triggered_by = ?",
a.NetworkID, a.TriggeredBy).Order("time_stamp DESC").Find(&ats).Error
return
}
func (a *Event) List(ctx context.Context) (ats []Event, err error) {
func (a *Event) List(ctx context.Context, from, to time.Time) (ats []Event, err error) {
if !from.IsZero() && !to.IsZero() {
err = db.FromContext(ctx).Model(&Event{}).Where("time_stamp BETWEEN ? AND ?", from, to).Order("time_stamp DESC").Find(&ats).Error
return
}
err = db.FromContext(ctx).Model(&Event{}).Order("time_stamp DESC").Find(&ats).Error
return
}
func (a *Event) DeleteOldEvents(ctx context.Context, retentionDays int) error {
cutoff := time.Now().AddDate(0, 0, -retentionDays)
return db.FromContext(ctx).Model(&Event{}).Where("created_at < ?", cutoff).Delete(&Event{}).Error
}

View file

@ -105,3 +105,4 @@ PUBLISH_METRIC_INTERVAL=15
# auto delete offline nodes
AUTO_DELETE_OFFLINE_NODES=false