diff --git a/management/server/account.go b/management/server/account.go index 8bb6bdba1..80906561d 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -63,7 +63,7 @@ type AccountManager interface { UpdatePeerSSHKey(peerKey string, sshKey string) error GetUsersFromAccount(accountID, userID string) ([]*UserInfo, error) GetGroup(accountId, groupID string) (*Group, error) - SaveGroup(accountId string, group *Group) error + SaveGroup(accountID, userID string, group *Group) error UpdateGroup(accountID string, groupID string, operations []GroupUpdateOperation) (*Group, error) DeleteGroup(accountId, groupID string) error ListGroups(accountId string) ([]*Group, error) diff --git a/management/server/activity/event.go b/management/server/activity/event.go index 14603af99..34b7ad040 100644 --- a/management/server/activity/event.go +++ b/management/server/activity/event.go @@ -29,6 +29,10 @@ const ( SetupKeyRevoked // SetupKeyOverused indicates that setup key usage exhausted SetupKeyOverused + // GroupCreated indicates that a user created a group + GroupCreated + // GroupUpdated indicates that a user updated a group + GroupUpdated ) const ( @@ -58,6 +62,10 @@ const ( SetupKeyRevokedMessage string = "Setup key revoked" // SetupKeyOverusedMessage is a human-readable text message of the SetupKeyOverused activity SetupKeyOverusedMessage string = "Setup key overused" + // GroupCreatedMessage is a human-readable text message of the GroupCreated activity + GroupCreatedMessage string = "Group created" + // GroupUpdatedMessage is a human-readable text message of the GroupUpdated activity + GroupUpdatedMessage string = "Group updated" ) // Activity that triggered an Event @@ -92,6 +100,10 @@ func (a Activity) Message() string { return SetupKeyRevokedMessage case SetupKeyOverused: return SetupKeyOverusedMessage + case GroupCreated: + return GroupCreatedMessage + case GroupUpdated: + return GroupUpdatedMessage default: return "UNKNOWN_ACTIVITY" } @@ -126,6 +138,10 @@ func (a Activity) StringCode() string { return "setupkey.overuse" case SetupKeyUpdated: return "setupkey.update" + case GroupCreated: + return "group.add" + case GroupUpdated: + return "group.update" default: return "UNKNOWN_ACTIVITY" } diff --git a/management/server/group.go b/management/server/group.go index cfc784487..8455ab4ea 100644 --- a/management/server/group.go +++ b/management/server/group.go @@ -1,6 +1,10 @@ package server -import "github.com/netbirdio/netbird/management/server/status" +import ( + "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/status" + "time" +) // Group of the peers for ACL type Group struct { @@ -34,6 +38,11 @@ type GroupUpdateOperation struct { Values []string } +// EventMeta returns activity event meta related to the group +func (g *Group) EventMeta() map[string]any { + return map[string]any{"name": g.Name} +} + func (g *Group) Copy() *Group { return &Group{ ID: g.ID, @@ -62,7 +71,7 @@ func (am *DefaultAccountManager) GetGroup(accountID, groupID string) (*Group, er } // SaveGroup object of the peers -func (am *DefaultAccountManager) SaveGroup(accountID string, group *Group) error { +func (am *DefaultAccountManager) SaveGroup(accountID, userID string, group *Group) error { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -71,7 +80,7 @@ func (am *DefaultAccountManager) SaveGroup(accountID string, group *Group) error if err != nil { return err } - + _, exists := account.Groups[group.ID] account.Groups[group.ID] = group account.Network.IncSerial() @@ -79,6 +88,20 @@ func (am *DefaultAccountManager) SaveGroup(accountID string, group *Group) error return err } + if !exists { + _, err = am.eventStore.Save(&activity.Event{ + Timestamp: time.Now(), + Activity: activity.GroupCreated, + InitiatorID: userID, + TargetID: group.ID, + AccountID: accountID, + Meta: group.EventMeta(), + }) + if err != nil { + return err + } + } + return am.updateAccountPeers(account) } diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 71e6842d1..f452cfb68 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -522,6 +522,7 @@ components: enum: [ "user.peer.delete", "user.join", "user.invite", "user.peer.add", "setupkey.peer.add", "setupkey.add", "setupkey.update", "setupkey.revoke", "setupkey.overuse", "rule.add", "rule.delete", "rule.update", + "group.add", "group.update", "account.create", ] initiator_id: diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index e723e37fc..e180265d0 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -14,6 +14,8 @@ const ( // Defines values for EventActivityCode. const ( EventActivityCodeAccountCreate EventActivityCode = "account.create" + EventActivityCodeGroupAdd EventActivityCode = "group.add" + EventActivityCodeGroupUpdate EventActivityCode = "group.update" EventActivityCodeRuleAdd EventActivityCode = "rule.add" EventActivityCodeRuleDelete EventActivityCode = "rule.delete" EventActivityCodeRuleUpdate EventActivityCode = "rule.update" diff --git a/management/server/http/groups.go b/management/server/http/groups.go index a636da375..8ee4f6bae 100644 --- a/management/server/http/groups.go +++ b/management/server/http/groups.go @@ -51,7 +51,7 @@ func (h *Groups) GetAllGroupsHandler(w http.ResponseWriter, r *http.Request) { // UpdateGroupHandler handles update to a group identified by a given ID func (h *Groups) UpdateGroupHandler(w http.ResponseWriter, r *http.Request) { claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience) - account, _, err := h.accountManager.GetAccountFromToken(claims) + account, user, err := h.accountManager.GetAccountFromToken(claims) if err != nil { util.WriteError(err, w) return @@ -102,7 +102,7 @@ func (h *Groups) UpdateGroupHandler(w http.ResponseWriter, r *http.Request) { Peers: peerIPsToKeys(account, req.Peers), } - if err := h.accountManager.SaveGroup(account.Id, &group); err != nil { + if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil { log.Errorf("failed updating group %s under account %s %v", groupID, account.Id, err) util.WriteError(err, w) return @@ -219,7 +219,7 @@ func (h *Groups) PatchGroupHandler(w http.ResponseWriter, r *http.Request) { // CreateGroupHandler handles group creation request func (h *Groups) CreateGroupHandler(w http.ResponseWriter, r *http.Request) { claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience) - account, _, err := h.accountManager.GetAccountFromToken(claims) + account, user, err := h.accountManager.GetAccountFromToken(claims) if err != nil { util.WriteError(err, w) return @@ -243,7 +243,7 @@ func (h *Groups) CreateGroupHandler(w http.ResponseWriter, r *http.Request) { Peers: peerIPsToKeys(account, req.Peers), } - err = h.accountManager.SaveGroup(account.Id, &group) + err = h.accountManager.SaveGroup(account.Id, user.Id, &group) if err != nil { util.WriteError(err, w) return diff --git a/management/server/setupkey.go b/management/server/setupkey.go index 84fe45d71..5df434db8 100644 --- a/management/server/setupkey.go +++ b/management/server/setupkey.go @@ -108,7 +108,7 @@ func (key *SetupKey) Copy() *SetupKey { } } -// EventMeta returns activity event meta related to the peer +// EventMeta returns activity event meta related to the setup key func (key *SetupKey) EventMeta() map[string]any { return map[string]any{"name": key.Name, "type": key.Type, "key": key.HiddenCopy(1).Key} }