diff --git a/management/cmd/management.go b/management/cmd/management.go index 82168ec72..93b487c63 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/google/uuid" "github.com/miekg/dns" + "github.com/netbirdio/netbird/management/server/event" httpapi "github.com/netbirdio/netbird/management/server/http" "github.com/netbirdio/netbird/management/server/metrics" "github.com/netbirdio/netbird/management/server/telemetry" @@ -142,7 +143,12 @@ var ( if disableSingleAccMode { mgmtSingleAccModeDomain = "" } - accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain, dnsDomain) + eventStore, err := event.NewSQLiteStore(config.Datadir) + if err != nil { + return err + } + accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain, + dnsDomain, eventStore) if err != nil { return fmt.Errorf("failed to build default manager: %v", err) } diff --git a/management/server/account.go b/management/server/account.go index 2d434e2d9..1df30dbf0 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -6,6 +6,7 @@ import ( "github.com/eko/gocache/v3/cache" cacheStore "github.com/eko/gocache/v3/store" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server/event" "github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/status" @@ -99,6 +100,7 @@ type DefaultAccountManager struct { idpManager idp.Manager cacheManager cache.CacheInterface[[]*idp.UserData] ctx context.Context + eventStore event.Store // singleAccountMode indicates whether the instance has a single account. // If true, then every new user will end up under the same account. @@ -390,7 +392,7 @@ func (a *Account) GetGroupAll() (*Group, error) { // BuildManager creates a new DefaultAccountManager with a provided Store func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManager idp.Manager, - singleAccountModeDomain string, dnsDomain string) (*DefaultAccountManager, error) { + singleAccountModeDomain string, dnsDomain string, eventStore event.Store) (*DefaultAccountManager, error) { am := &DefaultAccountManager{ Store: store, peersUpdateManager: peersUpdateManager, @@ -399,6 +401,7 @@ func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManage cacheMux: sync.Mutex{}, cacheLoading: map[string]chan struct{}{}, dnsDomain: dnsDomain, + eventStore: eventStore, } allAccounts := store.GetAllAccounts() // enable single account mode only if configured by user and number of existing accounts is not grater than 1 diff --git a/management/server/event/event.go b/management/server/event/event.go index 26cfbf531..925633398 100644 --- a/management/server/event/event.go +++ b/management/server/event/event.go @@ -9,9 +9,22 @@ const ( ManagementEvent Type = "management" ) +const ( + AddPeerByUserOperation Operation = iota + AddPeerWithKeyOperation +) + +const ( + AddPeerByUserOperationMessage string = "Add new peer" + AddPeerWithKeyOperationMessage string = AddPeerByUserOperationMessage +) + // Type of the Event type Type string +// Operation is an action that triggered an Event +type Operation int + // Store provides an interface to store or stream events. type Store interface { // Save an event in the store @@ -28,6 +41,8 @@ type Event struct { Timestamp time.Time // Operation that was performed during the event Operation string + // OperationCode that was performed during the event + OperationCode Operation // ID of the event (can be empty, meaning that it wasn't yet generated) ID uint64 // Type of the event diff --git a/management/server/event/sqlite.go b/management/server/event/sqlite.go index b1a86d3c2..266f60fa7 100644 --- a/management/server/event/sqlite.go +++ b/management/server/event/sqlite.go @@ -12,7 +12,7 @@ const ( SQLiteEventSinkDB = "events.db" createTableQuery = "CREATE TABLE IF NOT EXISTS events " + "(id INTEGER PRIMARY KEY AUTOINCREMENT, account TEXT NOT NULL, " + - "operation TEXT, " + + "operation INTEGER, " + "type TEXT, " + "timestamp DATETIME, " + "modifier TEXT," + @@ -98,7 +98,7 @@ func (store *SQLiteStore) Save(event Event) (*Event, error) { return nil, err } - result, err := stmt.Exec(event.Operation, event.Timestamp, event.ModifierID, event.TargetID, event.AccountID, event.Type) + result, err := stmt.Exec(event.OperationCode, event.Timestamp, event.ModifierID, event.TargetID, event.AccountID, event.Type) if err != nil { return nil, err } diff --git a/management/server/event/sqlite_test.go b/management/server/event/sqlite_test.go index 362913651..ae91022b5 100644 --- a/management/server/event/sqlite_test.go +++ b/management/server/event/sqlite_test.go @@ -19,12 +19,12 @@ func TestNewSQLiteStore(t *testing.T) { for i := 0; i < 10; i++ { _, err = store.Save(Event{ - Timestamp: time.Now(), - Operation: "cool_operation_" + fmt.Sprint(i), - Type: ManagementEvent, - ModifierID: "user_" + fmt.Sprint(i), - TargetID: "peer_" + fmt.Sprint(i), - AccountID: accountID, + Timestamp: time.Now(), + OperationCode: AddPeerByUserOperation, + Type: ManagementEvent, + ModifierID: "user_" + fmt.Sprint(i), + TargetID: "peer_" + fmt.Sprint(i), + AccountID: accountID, }) if err != nil { t.Fatal(err) diff --git a/management/server/peer.go b/management/server/peer.go index 01e7ffed4..9c3d41f53 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -2,6 +2,7 @@ package server import ( nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server/event" "github.com/netbirdio/netbird/management/server/status" "net" "strings" @@ -340,6 +341,7 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (* var account *Account var err error addedByUser := false + if len(userID) > 0 { addedByUser = true account, err = am.Store.GetAccountByUser(userID) @@ -359,6 +361,12 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (* return nil, err } + opEvent := event.Event{ + Timestamp: time.Now(), + Type: event.ManagementEvent, + AccountID: account.Id, + } + if !addedByUser { // validate the setup key if adding with a key sk, err := account.FindSetupKey(upperKey) @@ -371,6 +379,11 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (* } account.SetupKeys[sk.Key] = sk.IncrementUsage() + opEvent.ModifierID = sk.Id + opEvent.OperationCode = event.AddPeerWithKeyOperation + } else { + opEvent.ModifierID = userID + opEvent.OperationCode = event.AddPeerByUserOperation } takenIps := account.getTakenIPs() @@ -436,6 +449,12 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (* return nil, err } + opEvent.TargetID = newPeer.IP.String() + _, err = am.eventStore.Save(opEvent) + if err != nil { + return nil, err + } + return newPeer, nil }