diff --git a/management/cmd/management.go b/management/cmd/management.go index 93b487c63..6d6769fc6 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -9,7 +9,7 @@ import ( "fmt" "github.com/google/uuid" "github.com/miekg/dns" - "github.com/netbirdio/netbird/management/server/event" + "github.com/netbirdio/netbird/management/server/activity" httpapi "github.com/netbirdio/netbird/management/server/http" "github.com/netbirdio/netbird/management/server/metrics" "github.com/netbirdio/netbird/management/server/telemetry" @@ -143,7 +143,7 @@ var ( if disableSingleAccMode { mgmtSingleAccModeDomain = "" } - eventStore, err := event.NewSQLiteStore(config.Datadir) + eventStore, err := activity.NewSQLiteStore(config.Datadir) if err != nil { return err } diff --git a/management/server/account.go b/management/server/account.go index bd5690276..7475bc0d0 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -6,7 +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/activity" "github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/status" @@ -88,6 +88,7 @@ type AccountManager interface { DeleteNameServerGroup(accountID, nsGroupID string) error ListNameServerGroups(accountID string) ([]*nbdns.NameServerGroup, error) GetDNSDomain() string + GetEvents(accountID, userID string) ([]*activity.Event, error) } type DefaultAccountManager struct { @@ -100,7 +101,7 @@ type DefaultAccountManager struct { idpManager idp.Manager cacheManager cache.CacheInterface[[]*idp.UserData] ctx context.Context - eventStore event.Store + eventStore activity.Store // singleAccountMode indicates whether the instance has a single account. // If true, then every new user will end up under the same account. @@ -437,7 +438,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, eventStore event.Store) (*DefaultAccountManager, error) { + singleAccountModeDomain string, dnsDomain string, eventStore activity.Store) (*DefaultAccountManager, error) { am := &DefaultAccountManager{ Store: store, peersUpdateManager: peersUpdateManager, @@ -800,10 +801,10 @@ func (am *DefaultAccountManager) handleNewUserAccount(domainAcc *Account, claims return nil, err } - opEvent := event.Event{ + opEvent := &activity.Event{ Timestamp: time.Now(), - Type: event.ManagementEvent, - OperationCode: event.UserJoinedOperation, + Type: activity.ManagementEvent, + OperationCode: activity.UserJoinedOperation, AccountID: account.Id, TargetID: claims.UserId, ModifierID: claims.UserId, diff --git a/management/server/event/event.go b/management/server/activity/event.go similarity index 94% rename from management/server/event/event.go rename to management/server/activity/event.go index e3a650f05..d39083c6d 100644 --- a/management/server/event/event.go +++ b/management/server/activity/event.go @@ -1,4 +1,4 @@ -package event +package activity import "time" @@ -44,9 +44,9 @@ type Operation int // Store provides an interface to store or stream events. type Store interface { // Save an event in the store - Save(event Event) (*Event, error) + Save(event *Event) (*Event, error) // Get returns "limit" number of events from the "offset" index ordered descending or ascending by a timestamp - Get(accountID string, offset, limit int, descending bool) ([]Event, error) + Get(accountID string, offset, limit int, descending bool) ([]*Event, error) // Close the sink flushing events if necessary Close() error } diff --git a/management/server/event/sqlite.go b/management/server/activity/sqlite.go similarity index 80% rename from management/server/event/sqlite.go rename to management/server/activity/sqlite.go index 266f60fa7..626821fac 100644 --- a/management/server/event/sqlite.go +++ b/management/server/activity/sqlite.go @@ -1,4 +1,4 @@ -package event +package activity import ( "database/sql" @@ -19,7 +19,7 @@ const ( " target TEXT);" ) -// SQLiteStore is the implementation of the event.Store interface backed by SQLite +// SQLiteStore is the implementation of the activity.Store interface backed by SQLite type SQLiteStore struct { db *sql.DB } @@ -40,11 +40,11 @@ func NewSQLiteStore(dataDir string) (*SQLiteStore, error) { return &SQLiteStore{db: db}, nil } -func processResult(result *sql.Rows) ([]Event, error) { - events := make([]Event, 0) +func processResult(result *sql.Rows) ([]*Event, error) { + events := make([]*Event, 0) for result.Next() { var id int64 - var operation string + var operation Operation var timestamp time.Time var modifier string var target string @@ -55,14 +55,15 @@ func processResult(result *sql.Rows) ([]Event, error) { return nil, err } - events = append(events, Event{ - Timestamp: timestamp, - Operation: operation, - ID: uint64(id), - Type: typ, - ModifierID: modifier, - TargetID: target, - AccountID: account, + events = append(events, &Event{ + Timestamp: timestamp, + OperationCode: operation, + Operation: MessageForOperation(operation), + ID: uint64(id), + Type: typ, + ModifierID: modifier, + TargetID: target, + AccountID: account, }) } @@ -70,7 +71,7 @@ func processResult(result *sql.Rows) ([]Event, error) { } // Get returns "limit" number of events from index ordered descending or ascending by a timestamp -func (store *SQLiteStore) Get(accountID string, offset, limit int, descending bool) ([]Event, error) { +func (store *SQLiteStore) Get(accountID string, offset, limit int, descending bool) ([]*Event, error) { order := "DESC" if !descending { order = "ASC" @@ -91,7 +92,7 @@ func (store *SQLiteStore) Get(accountID string, offset, limit int, descending bo } // Save an event in the SQLite events table -func (store *SQLiteStore) Save(event Event) (*Event, error) { +func (store *SQLiteStore) Save(event *Event) (*Event, error) { stmt, err := store.db.Prepare("INSERT INTO events(operation, timestamp, modifier, target, account, type) VALUES(?, ?, ?, ?, ?, ?)") if err != nil { diff --git a/management/server/event/sqlite_test.go b/management/server/activity/sqlite_test.go similarity index 95% rename from management/server/event/sqlite_test.go rename to management/server/activity/sqlite_test.go index ae91022b5..df27f782a 100644 --- a/management/server/event/sqlite_test.go +++ b/management/server/activity/sqlite_test.go @@ -1,4 +1,4 @@ -package event +package activity import ( "fmt" @@ -18,7 +18,7 @@ func TestNewSQLiteStore(t *testing.T) { accountID := "account_1" for i := 0; i < 10; i++ { - _, err = store.Save(Event{ + _, err = store.Save(&Event{ Timestamp: time.Now(), OperationCode: AddPeerByUserOperation, Type: ManagementEvent, diff --git a/management/server/event.go b/management/server/event.go new file mode 100644 index 000000000..7f7b59583 --- /dev/null +++ b/management/server/event.go @@ -0,0 +1,8 @@ +package server + +import "github.com/netbirdio/netbird/management/server/activity" + +// GetEvents returns a list of activity events of an account +func (am *DefaultAccountManager) GetEvents(accountID, userID string) ([]*activity.Event, error) { + return am.eventStore.Get(accountID, 0, 1000, true) +} diff --git a/management/server/http/events.go b/management/server/http/events.go index 3331f14b2..e3f88c983 100644 --- a/management/server/http/events.go +++ b/management/server/http/events.go @@ -3,7 +3,7 @@ package http import ( "fmt" "github.com/netbirdio/netbird/management/server" - "github.com/netbirdio/netbird/management/server/event" + "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/http/api" "github.com/netbirdio/netbird/management/server/http/util" "github.com/netbirdio/netbird/management/server/jwtclaims" @@ -30,19 +30,28 @@ func NewEvents(accountManager server.AccountManager, authAudience string) *Event // GetEvents list of the given account func (h *Events) GetEvents(w http.ResponseWriter, r *http.Request) { claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience) - _, _, err := h.accountManager.GetAccountFromToken(claims) + account, user, err := h.accountManager.GetAccountFromToken(claims) if err != nil { log.Error(err) http.Redirect(w, r, "/", http.StatusInternalServerError) return } - var groups []*api.Event + accountEvents, err := h.accountManager.GetEvents(account.Id, user.Id) + if err != nil { + util.WriteError(err, w) + return + } + var events []*api.Event + for _, e := range accountEvents { + events = append(events, toEventResponse(e)) + } - util.WriteJSONObject(w, groups) + util.WriteJSONObject(w, events) } -func toEventResponse(event *event.Event) *api.Event { +func toEventResponse(event *activity.Event) *api.Event { + return &api.Event{ Id: fmt.Sprint(event.ID), InitiatorId: event.ModifierID, @@ -50,6 +59,6 @@ func toEventResponse(event *event.Event) *api.Event { OperationCode: int(event.OperationCode), TargetId: event.TargetID, Timestamp: event.Timestamp, - Type: event.Type, + Type: api.EventType(event.Type), } } diff --git a/management/server/http/handler.go b/management/server/http/handler.go index f0d0c5b4a..d4beaec90 100644 --- a/management/server/http/handler.go +++ b/management/server/http/handler.go @@ -40,6 +40,7 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience userHandler := NewUserHandler(accountManager, authAudience) routesHandler := NewRoutes(accountManager, authAudience) nameserversHandler := NewNameservers(accountManager, authAudience) + eventsHandler := NewEvents(accountManager, authAudience) apiHandler.HandleFunc("/peers", peersHandler.GetPeers).Methods("GET", "OPTIONS") apiHandler.HandleFunc("/peers/{id}", peersHandler.HandlePeer). @@ -81,6 +82,8 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience apiHandler.HandleFunc("/dns/nameservers/{id}", nameserversHandler.GetNameserverGroupHandler).Methods("GET", "OPTIONS") apiHandler.HandleFunc("/dns/nameservers/{id}", nameserversHandler.DeleteNameserverGroupHandler).Methods("DELETE", "OPTIONS") + apiHandler.HandleFunc("/events", eventsHandler.GetEvents).Methods("GET", "OPTIONS") + err = apiHandler.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { methods, err := route.GetMethods() if err != nil { diff --git a/management/server/peer.go b/management/server/peer.go index e8bd72249..a3ade59a6 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -2,7 +2,7 @@ package server import ( nbdns "github.com/netbirdio/netbird/dns" - "github.com/netbirdio/netbird/management/server/event" + "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/status" "net" "strings" @@ -361,9 +361,9 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (* return nil, err } - opEvent := event.Event{ + opEvent := &activity.Event{ Timestamp: time.Now(), - Type: event.ManagementEvent, + Type: activity.ManagementEvent, AccountID: account.Id, } @@ -380,10 +380,10 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (* account.SetupKeys[sk.Key] = sk.IncrementUsage() opEvent.ModifierID = sk.Id - opEvent.OperationCode = event.AddPeerWithKeyOperation + opEvent.OperationCode = activity.AddPeerWithKeyOperation } else { opEvent.ModifierID = userID - opEvent.OperationCode = event.AddPeerByUserOperation + opEvent.OperationCode = activity.AddPeerByUserOperation } takenIps := account.getTakenIPs()