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

@ -9,38 +9,39 @@ const (
)
type ServerSettings struct {
NetclientAutoUpdate bool `json:"netclientautoupdate"`
Verbosity int32 `json:"verbosity"`
AuthProvider string `json:"authprovider"`
OIDCIssuer string `json:"oidcissuer"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
SyncEnabled bool `json:"sync_enabled"`
GoogleAdminEmail string `json:"google_admin_email"`
GoogleSACredsJson string `json:"google_sa_creds_json"`
AzureTenant string `json:"azure_tenant"`
UserFilters []string `json:"user_filters"`
GroupFilters []string `json:"group_filters"`
IDPSyncInterval string `json:"idp_sync_interval"`
Telemetry string `json:"telemetry"`
BasicAuth bool `json:"basic_auth"`
JwtValidityDuration int `json:"jwt_validity_duration"`
RacAutoDisable bool `json:"rac_auto_disable"`
RacRestrictToSingleNetwork bool `json:"rac_restrict_to_single_network"`
EndpointDetection bool `json:"endpoint_detection"`
AllowedEmailDomains string `json:"allowed_email_domains"`
EmailSenderAddr string `json:"email_sender_addr"`
EmailSenderUser string `json:"email_sender_user"`
EmailSenderPassword string `json:"email_sender_password"`
SmtpHost string `json:"smtp_host"`
SmtpPort int `json:"smtp_port"`
MetricInterval string `json:"metric_interval"`
MetricsPort int `json:"metrics_port"`
ManageDNS bool `json:"manage_dns"`
DefaultDomain string `json:"default_domain"`
Stun bool `json:"stun"`
StunServers string `json:"stun_servers"`
Theme Theme `json:"theme"`
TextSize string `json:"text_size"`
ReducedMotion bool `json:"reduced_motion"`
NetclientAutoUpdate bool `json:"netclientautoupdate"`
Verbosity int32 `json:"verbosity"`
AuthProvider string `json:"authprovider"`
OIDCIssuer string `json:"oidcissuer"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
SyncEnabled bool `json:"sync_enabled"`
GoogleAdminEmail string `json:"google_admin_email"`
GoogleSACredsJson string `json:"google_sa_creds_json"`
AzureTenant string `json:"azure_tenant"`
UserFilters []string `json:"user_filters"`
GroupFilters []string `json:"group_filters"`
IDPSyncInterval string `json:"idp_sync_interval"`
Telemetry string `json:"telemetry"`
BasicAuth bool `json:"basic_auth"`
JwtValidityDuration int `json:"jwt_validity_duration"`
RacAutoDisable bool `json:"rac_auto_disable"`
RacRestrictToSingleNetwork bool `json:"rac_restrict_to_single_network"`
EndpointDetection bool `json:"endpoint_detection"`
AllowedEmailDomains string `json:"allowed_email_domains"`
EmailSenderAddr string `json:"email_sender_addr"`
EmailSenderUser string `json:"email_sender_user"`
EmailSenderPassword string `json:"email_sender_password"`
SmtpHost string `json:"smtp_host"`
SmtpPort int `json:"smtp_port"`
MetricInterval string `json:"metric_interval"`
MetricsPort int `json:"metrics_port"`
ManageDNS bool `json:"manage_dns"`
DefaultDomain string `json:"default_domain"`
Stun bool `json:"stun"`
StunServers string `json:"stun_servers"`
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