diff --git a/dns/nameserver.go b/dns/nameserver.go index 2af633354..807df5907 100644 --- a/dns/nameserver.go +++ b/dns/nameserver.go @@ -77,6 +77,11 @@ type NameServer struct { Port int } +// EventMeta returns activity event meta related to the nameserver group +func (g *NameServerGroup) EventMeta() map[string]any { + return map[string]any{"name": g.Name} +} + // Copy copies a nameserver object func (n *NameServer) Copy() *NameServer { return &NameServer{ diff --git a/management/server/account.go b/management/server/account.go index 289bfcf4d..5c49c582c 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -55,7 +55,7 @@ type AccountManager interface { MarkPeerConnected(peerKey string, connected bool) error DeletePeer(accountID, peerKey, userID string) (*Peer, error) GetPeerByIP(accountId string, peerIP string) (*Peer, error) - UpdatePeer(accountID string, peer *Peer) (*Peer, error) + UpdatePeer(accountID, userID string, peer *Peer) (*Peer, error) GetNetworkMap(peerKey string) (*NetworkMap, error) GetPeerNetwork(peerKey string) (*Network, error) AddPeer(setupKey, userID string, peer *Peer) (*Peer, error) @@ -76,16 +76,16 @@ type AccountManager interface { DeleteRule(accountID, ruleID, userID string) error ListRules(accountID, userID string) ([]*Rule, error) GetRoute(accountID, routeID, userID string) (*route.Route, error) - CreateRoute(accountID string, prefix, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) - SaveRoute(accountID string, route *route.Route) error - UpdateRoute(accountID string, routeID string, operations []RouteUpdateOperation) (*route.Route, error) - DeleteRoute(accountID, routeID string) error + CreateRoute(accountID string, prefix, peerIP, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error) + SaveRoute(accountID, userID string, route *route.Route) error + UpdateRoute(accountID, routeID string, operations []RouteUpdateOperation) (*route.Route, error) + DeleteRoute(accountID, routeID, userID string) error ListRoutes(accountID, userID string) ([]*route.Route, error) GetNameServerGroup(accountID, nsGroupID string) (*nbdns.NameServerGroup, error) - CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool) (*nbdns.NameServerGroup, error) - SaveNameServerGroup(accountID string, nsGroupToSave *nbdns.NameServerGroup) error - UpdateNameServerGroup(accountID, nsGroupID string, operations []NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) - DeleteNameServerGroup(accountID, nsGroupID string) error + CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string) (*nbdns.NameServerGroup, error) + SaveNameServerGroup(accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error + UpdateNameServerGroup(accountID, nsGroupID, userID string, operations []NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) + DeleteNameServerGroup(accountID, nsGroupID, userID string) error ListNameServerGroups(accountID string) ([]*nbdns.NameServerGroup, error) GetDNSDomain() string GetEvents(accountID, userID string) ([]*activity.Event, error) @@ -220,6 +220,17 @@ func (a *Account) GetRoutesByPrefix(prefix netip.Prefix) []*route.Route { return routes } +// GetPeerByIP returns peer by it's IP if exists under account or nil otherwise +func (a *Account) GetPeerByIP(peerIP string) *Peer { + for _, peer := range a.Peers { + if peerIP == peer.IP.String() { + return peer + } + } + + return nil +} + // GetPeerRules returns a list of source or destination rules of a given peer. func (a *Account) GetPeerRules(peerPubKey string) (srcRules []*Rule, dstRules []*Rule) { diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index 3cae2bd64..96cadd99d 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -49,6 +49,24 @@ const ( GroupAddedToDisabledManagementGroups // GroupRemovedFromDisabledManagementGroups indicates that a user removed a group from the DNS setting Disabled management groups GroupRemovedFromDisabledManagementGroups + // RouteCreated indicates that a user created a route + RouteCreated + // RouteRemoved indicates that a user deleted a route + RouteRemoved + // RouteUpdated indicates that a user updated a route + RouteUpdated + // PeerSSHEnabled indicates that a user enabled SSH server on a peer + PeerSSHEnabled + // PeerSSHDisabled indicates that a user disabled SSH server on a peer + PeerSSHDisabled + // PeerRenamed indicates that a user renamed a peer + PeerRenamed + // NameserverGroupCreated indicates that a user created a nameservers group + NameserverGroupCreated + // NameserverGroupDeleted indicates that a user deleted a nameservers group + NameserverGroupDeleted + // NameserverGroupUpdated indicates that a user updated a nameservers group + NameserverGroupUpdated ) const ( @@ -100,6 +118,24 @@ const ( GroupAddedToDisabledManagementGroupsMessage string = "Group added to disabled management DNS setting" // GroupRemovedFromDisabledManagementGroupsMessage is a human-readable text message of the GroupRemovedFromDisabledManagementGroups activity GroupRemovedFromDisabledManagementGroupsMessage string = "Group removed from disabled management DNS setting" + // RouteCreatedMessage is a human-readable text message of the RouteCreated activity + RouteCreatedMessage string = "Route created" + // RouteRemovedMessage is a human-readable text message of the RouteRemoved activity + RouteRemovedMessage string = "Route deleted" + // RouteUpdatedMessage is a human-readable text message of the RouteUpdated activity + RouteUpdatedMessage string = "Route updated" + // PeerSSHEnabledMessage is a human-readable text message of the PeerSSHEnabled activity + PeerSSHEnabledMessage string = "Peer SSH server enabled" + // PeerSSHDisabledMessage is a human-readable text message of the PeerSSHDisabled activity + PeerSSHDisabledMessage string = "Peer SSH server disabled" + // PeerRenamedMessage is a human-readable text message of the PeerRenamed activity + PeerRenamedMessage string = "Peer renamed" + // NameserverGroupCreatedMessage is a human-readable text message of the NameserverGroupCreated activity + NameserverGroupCreatedMessage string = "Nameserver group created" + // NameserverGroupDeletedMessage is a human-readable text message of the NameserverGroupDeleted activity + NameserverGroupDeletedMessage string = "Nameserver group deleted" + // NameserverGroupUpdatedMessage is a human-readable text message of the NameserverGroupUpdated activity + NameserverGroupUpdatedMessage string = "Nameserver group updated" ) // Activity that triggered an Event @@ -156,6 +192,24 @@ func (a Activity) Message() string { return GroupAddedToDisabledManagementGroupsMessage case GroupRemovedFromDisabledManagementGroups: return GroupRemovedFromDisabledManagementGroupsMessage + case RouteCreated: + return RouteCreatedMessage + case RouteRemoved: + return RouteRemovedMessage + case RouteUpdated: + return RouteUpdatedMessage + case PeerSSHEnabled: + return PeerSSHEnabledMessage + case PeerSSHDisabled: + return PeerSSHDisabledMessage + case PeerRenamed: + return PeerRenamedMessage + case NameserverGroupCreated: + return NameserverGroupCreatedMessage + case NameserverGroupDeleted: + return NameserverGroupDeletedMessage + case NameserverGroupUpdated: + return NameserverGroupUpdatedMessage default: return "UNKNOWN_ACTIVITY" } @@ -212,6 +266,24 @@ func (a Activity) StringCode() string { return "dns.setting.disabled.management.group.add" case GroupRemovedFromDisabledManagementGroups: return "dns.setting.disabled.management.group.delete" + case RouteCreated: + return "route.add" + case RouteRemoved: + return "route.delete" + case RouteUpdated: + return "route.update" + case PeerRenamed: + return "peer.rename" + case PeerSSHEnabled: + return "peer.ssh.enable" + case PeerSSHDisabled: + return "peer.ssh.disable" + case NameserverGroupCreated: + return "nameserver.group.add" + case NameserverGroupDeleted: + return "nameserver.group.delete" + case NameserverGroupUpdated: + return "nameserver.group.update" default: return "UNKNOWN_ACTIVITY" } diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 79b8e9135..17c71662f 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -535,7 +535,10 @@ components: "setupkey.group.delete", "setupkey.group.add", "rule.add", "rule.delete", "rule.update", "group.add", "group.update", "dns.setting.disabled.management.group.add", - "account.create", "dns.setting.disabled.management.group.delete" + "account.create", "dns.setting.disabled.management.group.delete", + "route.add", "route.delete", "route.update", + "nameserver.group.add", "nameserver.group.delete", "nameserver.group.update", + "peer.ssh.disable", "peer.ssh.enable", "peer.rename" ] initiator_id: description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event. diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index 550d44731..a7934c20f 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -18,6 +18,15 @@ const ( EventActivityCodeDnsSettingDisabledManagementGroupDelete EventActivityCode = "dns.setting.disabled.management.group.delete" EventActivityCodeGroupAdd EventActivityCode = "group.add" EventActivityCodeGroupUpdate EventActivityCode = "group.update" + EventActivityCodeNameserverGroupAdd EventActivityCode = "nameserver.group.add" + EventActivityCodeNameserverGroupDelete EventActivityCode = "nameserver.group.delete" + EventActivityCodeNameserverGroupUpdate EventActivityCode = "nameserver.group.update" + EventActivityCodePeerRename EventActivityCode = "peer.rename" + EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable" + EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable" + EventActivityCodeRouteAdd EventActivityCode = "route.add" + EventActivityCodeRouteDelete EventActivityCode = "route.delete" + EventActivityCodeRouteUpdate EventActivityCode = "route.update" EventActivityCodeRuleAdd EventActivityCode = "rule.add" EventActivityCodeRuleDelete EventActivityCode = "rule.delete" EventActivityCodeRuleUpdate EventActivityCode = "rule.update" diff --git a/management/server/http/nameservers.go b/management/server/http/nameservers.go index 77b39c87a..05ff45ece 100644 --- a/management/server/http/nameservers.go +++ b/management/server/http/nameservers.go @@ -57,7 +57,7 @@ func (h *Nameservers) GetAllNameserversHandler(w http.ResponseWriter, r *http.Re // CreateNameserverGroupHandler handles nameserver group creation request func (h *Nameservers) CreateNameserverGroupHandler(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 @@ -76,7 +76,7 @@ func (h *Nameservers) CreateNameserverGroupHandler(w http.ResponseWriter, r *htt return } - nsGroup, err := h.accountManager.CreateNameServerGroup(account.Id, req.Name, req.Description, nsList, req.Groups, req.Primary, req.Domains, req.Enabled) + nsGroup, err := h.accountManager.CreateNameServerGroup(account.Id, req.Name, req.Description, nsList, req.Groups, req.Primary, req.Domains, req.Enabled, user.Id) if err != nil { util.WriteError(err, w) return @@ -90,7 +90,7 @@ func (h *Nameservers) CreateNameserverGroupHandler(w http.ResponseWriter, r *htt // UpdateNameserverGroupHandler handles update to a nameserver group identified by a given ID func (h *Nameservers) UpdateNameserverGroupHandler(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 @@ -126,7 +126,7 @@ func (h *Nameservers) UpdateNameserverGroupHandler(w http.ResponseWriter, r *htt Enabled: req.Enabled, } - err = h.accountManager.SaveNameServerGroup(account.Id, updatedNSGroup) + err = h.accountManager.SaveNameServerGroup(account.Id, user.Id, updatedNSGroup) if err != nil { util.WriteError(err, w) return @@ -140,7 +140,7 @@ func (h *Nameservers) UpdateNameserverGroupHandler(w http.ResponseWriter, r *htt // PatchNameserverGroupHandler handles patch updates to a nameserver group identified by a given ID func (h *Nameservers) PatchNameserverGroupHandler(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 @@ -208,7 +208,7 @@ func (h *Nameservers) PatchNameserverGroupHandler(w http.ResponseWriter, r *http } } - updatedNSGroup, err := h.accountManager.UpdateNameServerGroup(account.Id, nsGroupID, operations) + updatedNSGroup, err := h.accountManager.UpdateNameServerGroup(account.Id, nsGroupID, user.Id, operations) if err != nil { util.WriteError(err, w) return @@ -222,7 +222,7 @@ func (h *Nameservers) PatchNameserverGroupHandler(w http.ResponseWriter, r *http // DeleteNameserverGroupHandler handles nameserver group deletion request func (h *Nameservers) DeleteNameserverGroupHandler(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 @@ -234,7 +234,7 @@ func (h *Nameservers) DeleteNameserverGroupHandler(w http.ResponseWriter, r *htt return } - err = h.accountManager.DeleteNameServerGroup(account.Id, nsGroupID) + err = h.accountManager.DeleteNameServerGroup(account.Id, nsGroupID, user.Id) if err != nil { util.WriteError(err, w) return diff --git a/management/server/http/nameservers_test.go b/management/server/http/nameservers_test.go index 037de8155..2a2954e7d 100644 --- a/management/server/http/nameservers_test.go +++ b/management/server/http/nameservers_test.go @@ -63,7 +63,7 @@ func initNameserversTestData() *Nameservers { } return nil, status.Errorf(status.NotFound, "nameserver group with ID %s not found", nsGroupID) }, - CreateNameServerGroupFunc: func(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool) (*nbdns.NameServerGroup, error) { + CreateNameServerGroupFunc: func(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, _ string) (*nbdns.NameServerGroup, error) { return &nbdns.NameServerGroup{ ID: existingNSGroupID, Name: name, @@ -75,16 +75,16 @@ func initNameserversTestData() *Nameservers { Domains: domains, }, nil }, - DeleteNameServerGroupFunc: func(accountID, nsGroupID string) error { + DeleteNameServerGroupFunc: func(accountID, nsGroupID, _ string) error { return nil }, - SaveNameServerGroupFunc: func(accountID string, nsGroupToSave *nbdns.NameServerGroup) error { + SaveNameServerGroupFunc: func(accountID, _ string, nsGroupToSave *nbdns.NameServerGroup) error { if nsGroupToSave.ID == existingNSGroupID { return nil } return status.Errorf(status.NotFound, "nameserver group with ID %s was not found", nsGroupToSave.ID) }, - UpdateNameServerGroupFunc: func(accountID, nsGroupID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) { + UpdateNameServerGroupFunc: func(accountID, nsGroupID, _ string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) { nsGroupToUpdate := baseExistingNSGroup.Copy() if nsGroupID != nsGroupToUpdate.ID { return nil, status.Errorf(status.NotFound, "nameserver group ID %s no longer exists", nsGroupID) @@ -110,7 +110,7 @@ func initNameserversTestData() *Nameservers { return nsGroupToUpdate, nil }, GetAccountFromTokenFunc: func(_ jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) { - return testingNSAccount, nil, nil + return testingNSAccount, testingAccount.Users["test_user"], nil }, }, authAudience: "", diff --git a/management/server/http/peers.go b/management/server/http/peers.go index 0962b8b7d..d6c36944f 100644 --- a/management/server/http/peers.go +++ b/management/server/http/peers.go @@ -27,7 +27,7 @@ func NewPeers(accountManager server.AccountManager, authAudience string) *Peers } } -func (h *Peers) updatePeer(account *server.Account, peer *server.Peer, w http.ResponseWriter, r *http.Request) { +func (h *Peers) updatePeer(account *server.Account, user *server.User, peer *server.Peer, w http.ResponseWriter, r *http.Request) { req := &api.PutApiPeersIdJSONBody{} err := json.NewDecoder(r.Body).Decode(&req) if err != nil { @@ -36,7 +36,7 @@ func (h *Peers) updatePeer(account *server.Account, peer *server.Peer, w http.Re } update := &server.Peer{Key: peer.Key, SSHEnabled: req.SshEnabled, Name: req.Name} - peer, err = h.accountManager.UpdatePeer(account.Id, update) + peer, err = h.accountManager.UpdatePeer(account.Id, user.Id, update) if err != nil { util.WriteError(err, w) return @@ -81,7 +81,7 @@ func (h *Peers) HandlePeer(w http.ResponseWriter, r *http.Request) { h.deletePeer(account.Id, user.Id, peer, w, r) return case http.MethodPut: - h.updatePeer(account, peer, w, r) + h.updatePeer(account, user, peer, w, r) return case http.MethodGet: util.WriteJSONObject(w, toPeerResponse(peer, account, dnsDomain)) diff --git a/management/server/http/routes.go b/management/server/http/routes.go index 8145ba0e1..5f6bd09e8 100644 --- a/management/server/http/routes.go +++ b/management/server/http/routes.go @@ -54,7 +54,7 @@ func (h *Routes) GetAllRoutesHandler(w http.ResponseWriter, r *http.Request) { // CreateRouteHandler handles route creation request func (h *Routes) CreateRouteHandler(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 @@ -67,16 +67,6 @@ func (h *Routes) CreateRouteHandler(w http.ResponseWriter, r *http.Request) { return } - peerKey := req.Peer - if req.Peer != "" { - peer, err := h.accountManager.GetPeerByIP(account.Id, req.Peer) - if err != nil { - util.WriteError(err, w) - return - } - peerKey = peer.Key - } - _, newPrefix, err := route.ParseNetwork(req.Network) if err != nil { util.WriteError(err, w) @@ -89,7 +79,7 @@ func (h *Routes) CreateRouteHandler(w http.ResponseWriter, r *http.Request) { return } - newRoute, err := h.accountManager.CreateRoute(account.Id, newPrefix.String(), peerKey, req.Description, req.NetworkId, req.Masquerade, req.Metric, req.Groups, req.Enabled) + newRoute, err := h.accountManager.CreateRoute(account.Id, newPrefix.String(), req.Peer, req.Description, req.NetworkId, req.Masquerade, req.Metric, req.Groups, req.Enabled, user.Id) if err != nil { util.WriteError(err, w) return @@ -138,9 +128,9 @@ func (h *Routes) UpdateRouteHandler(w http.ResponseWriter, r *http.Request) { peerKey := req.Peer if req.Peer != "" { - peer, err := h.accountManager.GetPeerByIP(account.Id, req.Peer) - if err != nil { - util.WriteError(err, w) + peer := account.GetPeerByIP(req.Peer) + if peer == nil { + util.WriteError(status.Errorf(status.NotFound, "peer %s not found", req.Peer), w) return } peerKey = peer.Key @@ -165,7 +155,7 @@ func (h *Routes) UpdateRouteHandler(w http.ResponseWriter, r *http.Request) { Groups: req.Groups, } - err = h.accountManager.SaveRoute(account.Id, newRoute) + err = h.accountManager.SaveRoute(account.Id, user.Id, newRoute) if err != nil { util.WriteError(err, w) return @@ -329,7 +319,7 @@ func (h *Routes) PatchRouteHandler(w http.ResponseWriter, r *http.Request) { // DeleteRouteHandler handles route deletion request func (h *Routes) DeleteRouteHandler(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 @@ -341,7 +331,7 @@ func (h *Routes) DeleteRouteHandler(w http.ResponseWriter, r *http.Request) { return } - err = h.accountManager.DeleteRoute(account.Id, routeID) + err = h.accountManager.DeleteRoute(account.Id, routeID, user.Id) if err != nil { util.WriteError(err, w) return diff --git a/management/server/http/routes_test.go b/management/server/http/routes_test.go index 9af5e7f55..ef4011fd1 100644 --- a/management/server/http/routes_test.go +++ b/management/server/http/routes_test.go @@ -48,7 +48,7 @@ var testingAccount = &server.Account{ Domain: "hotmail.com", Peers: map[string]*server.Peer{ existingPeerKey: { - Key: existingPeerID, + Key: existingPeerKey, IP: netip.MustParseAddr(existingPeerID).AsSlice(), }, }, @@ -66,12 +66,18 @@ func initRoutesTestData() *Routes { } return nil, status.Errorf(status.NotFound, "route with ID %s not found", routeID) }, - CreateRouteFunc: func(accountID string, network, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) { + CreateRouteFunc: func(accountID string, network, peerIP, description, netID string, masquerade bool, metric int, groups []string, enabled bool, _ string) (*route.Route, error) { + + peer := testingAccount.GetPeerByIP(peerIP) + if peer == nil { + return nil, status.Errorf(status.NotFound, "peer %s not found", peerIP) + } + networkType, p, _ := route.ParseNetwork(network) return &route.Route{ ID: existingRouteID, NetID: netID, - Peer: peer, + Peer: peer.Key, Network: p, NetworkType: networkType, Description: description, @@ -80,12 +86,12 @@ func initRoutesTestData() *Routes { Groups: groups, }, nil }, - SaveRouteFunc: func(_ string, _ *route.Route) error { + SaveRouteFunc: func(_, _ string, _ *route.Route) error { return nil }, - DeleteRouteFunc: func(_ string, peerIP string) error { - if peerIP != existingRouteID { - return status.Errorf(status.NotFound, "Peer with ID %s not found", peerIP) + DeleteRouteFunc: func(_ string, routeID string, _ string) error { + if routeID != existingRouteID { + return status.Errorf(status.NotFound, "Peer with ID %s not found", routeID) } return nil }, diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 7edefd1ca..81186a40d 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -44,21 +44,21 @@ type MockAccountManager struct { GetUsersFromAccountFunc func(accountID, userID string) ([]*server.UserInfo, error) UpdatePeerMetaFunc func(peerKey string, meta server.PeerSystemMeta) error UpdatePeerSSHKeyFunc func(peerKey string, sshKey string) error - UpdatePeerFunc func(accountID string, peer *server.Peer) (*server.Peer, error) - CreateRouteFunc func(accountID string, prefix, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) + UpdatePeerFunc func(accountID, userID string, peer *server.Peer) (*server.Peer, error) + CreateRouteFunc func(accountID string, prefix, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error) GetRouteFunc func(accountID, routeID, userID string) (*route.Route, error) - SaveRouteFunc func(accountID string, route *route.Route) error + SaveRouteFunc func(accountID, userID string, route *route.Route) error UpdateRouteFunc func(accountID string, routeID string, operations []server.RouteUpdateOperation) (*route.Route, error) - DeleteRouteFunc func(accountID, routeID string) error + DeleteRouteFunc func(accountID, routeID, userID string) error ListRoutesFunc func(accountID, userID string) ([]*route.Route, error) SaveSetupKeyFunc func(accountID string, key *server.SetupKey, userID string) (*server.SetupKey, error) ListSetupKeysFunc func(accountID, userID string) ([]*server.SetupKey, error) SaveUserFunc func(accountID, userID string, user *server.User) (*server.UserInfo, error) GetNameServerGroupFunc func(accountID, nsGroupID string) (*nbdns.NameServerGroup, error) - CreateNameServerGroupFunc func(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool) (*nbdns.NameServerGroup, error) - SaveNameServerGroupFunc func(accountID string, nsGroupToSave *nbdns.NameServerGroup) error - UpdateNameServerGroupFunc func(accountID, nsGroupID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) - DeleteNameServerGroupFunc func(accountID, nsGroupID string) error + CreateNameServerGroupFunc func(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string) (*nbdns.NameServerGroup, error) + SaveNameServerGroupFunc func(accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error + UpdateNameServerGroupFunc func(accountID, nsGroupID, userID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) + DeleteNameServerGroupFunc func(accountID, nsGroupID, userID string) error ListNameServerGroupsFunc func(accountID string) ([]*nbdns.NameServerGroup, error) CreateUserFunc func(accountID, userID string, key *server.UserInfo) (*server.UserInfo, error) GetAccountFromTokenFunc func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) @@ -323,17 +323,17 @@ func (am *MockAccountManager) UpdatePeerSSHKey(peerKey string, sshKey string) er } // UpdatePeer mocks UpdatePeerFunc function of the account manager -func (am *MockAccountManager) UpdatePeer(accountID string, peer *server.Peer) (*server.Peer, error) { +func (am *MockAccountManager) UpdatePeer(accountID, userID string, peer *server.Peer) (*server.Peer, error) { if am.UpdatePeerFunc != nil { - return am.UpdatePeerFunc(accountID, peer) + return am.UpdatePeerFunc(accountID, userID, peer) } return nil, status.Errorf(codes.Unimplemented, "method UpdatePeerFunc is is not implemented") } // CreateRoute mock implementation of CreateRoute from server.AccountManager interface -func (am *MockAccountManager) CreateRoute(accountID string, network, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) { +func (am *MockAccountManager) CreateRoute(accountID string, network, peerIP, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error) { if am.CreateRouteFunc != nil { - return am.CreateRouteFunc(accountID, network, peer, description, netID, masquerade, metric, groups, enabled) + return am.CreateRouteFunc(accountID, network, peerIP, description, netID, masquerade, metric, groups, enabled, userID) } return nil, status.Errorf(codes.Unimplemented, "method CreateRoute is not implemented") } @@ -347,15 +347,15 @@ func (am *MockAccountManager) GetRoute(accountID, routeID, userID string) (*rout } // SaveRoute mock implementation of SaveRoute from server.AccountManager interface -func (am *MockAccountManager) SaveRoute(accountID string, route *route.Route) error { +func (am *MockAccountManager) SaveRoute(accountID, userID string, route *route.Route) error { if am.SaveRouteFunc != nil { - return am.SaveRouteFunc(accountID, route) + return am.SaveRouteFunc(accountID, userID, route) } return status.Errorf(codes.Unimplemented, "method SaveRoute is not implemented") } // UpdateRoute mock implementation of UpdateRoute from server.AccountManager interface -func (am *MockAccountManager) UpdateRoute(accountID string, ruleID string, operations []server.RouteUpdateOperation) (*route.Route, error) { +func (am *MockAccountManager) UpdateRoute(accountID, ruleID string, operations []server.RouteUpdateOperation) (*route.Route, error) { if am.UpdateRouteFunc != nil { return am.UpdateRouteFunc(accountID, ruleID, operations) } @@ -363,9 +363,9 @@ func (am *MockAccountManager) UpdateRoute(accountID string, ruleID string, opera } // DeleteRoute mock implementation of DeleteRoute from server.AccountManager interface -func (am *MockAccountManager) DeleteRoute(accountID, routeID string) error { +func (am *MockAccountManager) DeleteRoute(accountID, routeID, userID string) error { if am.DeleteRouteFunc != nil { - return am.DeleteRouteFunc(accountID, routeID) + return am.DeleteRouteFunc(accountID, routeID, userID) } return status.Errorf(codes.Unimplemented, "method DeleteRoute is not implemented") } @@ -422,33 +422,33 @@ func (am *MockAccountManager) GetNameServerGroup(accountID, nsGroupID string) (* } // CreateNameServerGroup mocks CreateNameServerGroup of the AccountManager interface -func (am *MockAccountManager) CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool) (*nbdns.NameServerGroup, error) { +func (am *MockAccountManager) CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string) (*nbdns.NameServerGroup, error) { if am.CreateNameServerGroupFunc != nil { - return am.CreateNameServerGroupFunc(accountID, name, description, nameServerList, groups, primary, domains, enabled) + return am.CreateNameServerGroupFunc(accountID, name, description, nameServerList, groups, primary, domains, enabled, userID) } return nil, nil } // SaveNameServerGroup mocks SaveNameServerGroup of the AccountManager interface -func (am *MockAccountManager) SaveNameServerGroup(accountID string, nsGroupToSave *nbdns.NameServerGroup) error { +func (am *MockAccountManager) SaveNameServerGroup(accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error { if am.SaveNameServerGroupFunc != nil { - return am.SaveNameServerGroupFunc(accountID, nsGroupToSave) + return am.SaveNameServerGroupFunc(accountID, userID, nsGroupToSave) } return nil } // UpdateNameServerGroup mocks UpdateNameServerGroup of the AccountManager interface -func (am *MockAccountManager) UpdateNameServerGroup(accountID, nsGroupID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) { +func (am *MockAccountManager) UpdateNameServerGroup(accountID, nsGroupID, userID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) { if am.UpdateNameServerGroupFunc != nil { - return am.UpdateNameServerGroupFunc(accountID, nsGroupID, operations) + return am.UpdateNameServerGroupFunc(accountID, nsGroupID, userID, operations) } return nil, nil } // DeleteNameServerGroup mocks DeleteNameServerGroup of the AccountManager interface -func (am *MockAccountManager) DeleteNameServerGroup(accountID, nsGroupID string) error { +func (am *MockAccountManager) DeleteNameServerGroup(accountID, nsGroupID, userID string) error { if am.DeleteNameServerGroupFunc != nil { - return am.DeleteNameServerGroupFunc(accountID, nsGroupID) + return am.DeleteNameServerGroupFunc(accountID, nsGroupID, userID) } return nil } diff --git a/management/server/nameserver.go b/management/server/nameserver.go index 4c4190c77..5569172c4 100644 --- a/management/server/nameserver.go +++ b/management/server/nameserver.go @@ -3,6 +3,7 @@ package server import ( "github.com/miekg/dns" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/status" "github.com/rs/xid" log "github.com/sirupsen/logrus" @@ -77,7 +78,7 @@ func (am *DefaultAccountManager) GetNameServerGroup(accountID, nsGroupID string) } // CreateNameServerGroup creates and saves a new nameserver group -func (am *DefaultAccountManager) CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool) (*nbdns.NameServerGroup, error) { +func (am *DefaultAccountManager) CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string) (*nbdns.NameServerGroup, error) { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -121,11 +122,13 @@ func (am *DefaultAccountManager) CreateNameServerGroup(accountID string, name, d return newNSGroup.Copy(), status.Errorf(status.Internal, "failed to update peers after create nameserver %s", name) } + am.storeEvent(userID, newNSGroup.ID, accountID, activity.NameserverGroupCreated, newNSGroup.EventMeta()) + return newNSGroup.Copy(), nil } // SaveNameServerGroup saves nameserver group -func (am *DefaultAccountManager) SaveNameServerGroup(accountID string, nsGroupToSave *nbdns.NameServerGroup) error { +func (am *DefaultAccountManager) SaveNameServerGroup(accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -158,11 +161,13 @@ func (am *DefaultAccountManager) SaveNameServerGroup(accountID string, nsGroupTo return status.Errorf(status.Internal, "failed to update peers after update nameserver %s", nsGroupToSave.Name) } + am.storeEvent(userID, nsGroupToSave.ID, accountID, activity.NameserverGroupUpdated, nsGroupToSave.EventMeta()) + return nil } // UpdateNameServerGroup updates existing nameserver group with set of operations -func (am *DefaultAccountManager) UpdateNameServerGroup(accountID, nsGroupID string, operations []NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) { +func (am *DefaultAccountManager) UpdateNameServerGroup(accountID, nsGroupID, userID string, operations []NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -265,7 +270,7 @@ func (am *DefaultAccountManager) UpdateNameServerGroup(accountID, nsGroupID stri } // DeleteNameServerGroup deletes nameserver group with nsGroupID -func (am *DefaultAccountManager) DeleteNameServerGroup(accountID, nsGroupID string) error { +func (am *DefaultAccountManager) DeleteNameServerGroup(accountID, nsGroupID, userID string) error { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -275,6 +280,10 @@ func (am *DefaultAccountManager) DeleteNameServerGroup(accountID, nsGroupID stri return err } + nsGroup := account.NameServerGroups[nsGroupID] + if nsGroup == nil { + return status.Errorf(status.NotFound, "nameserver group %s wasn't found", nsGroupID) + } delete(account.NameServerGroups, nsGroupID) account.Network.IncSerial() @@ -285,10 +294,11 @@ func (am *DefaultAccountManager) DeleteNameServerGroup(accountID, nsGroupID stri err = am.updateAccountPeers(account) if err != nil { - log.Error(err) return status.Errorf(status.Internal, "failed to update peers after deleting nameserver %s", nsGroupID) } + am.storeEvent(userID, nsGroup.ID, accountID, activity.NameserverGroupDeleted, nsGroup.EventMeta()) + return nil } diff --git a/management/server/nameserver_test.go b/management/server/nameserver_test.go index a05ad49f7..71fa5e814 100644 --- a/management/server/nameserver_test.go +++ b/management/server/nameserver_test.go @@ -380,6 +380,7 @@ func TestCreateNameServerGroup(t *testing.T) { testCase.inputArgs.primary, testCase.inputArgs.domains, testCase.inputArgs.enabled, + userID, ) testCase.errFunc(t, err) @@ -628,7 +629,7 @@ func TestSaveNameServerGroup(t *testing.T) { } } - err = am.SaveNameServerGroup(account.Id, nsGroupToSave) + err = am.SaveNameServerGroup(account.Id, userID, nsGroupToSave) testCase.errFunc(t, err) @@ -952,7 +953,7 @@ func TestUpdateNameServerGroup(t *testing.T) { t.Error("account should be saved") } - updatedRoute, err := am.UpdateNameServerGroup(account.Id, testCase.nsGroupID, testCase.operations) + updatedRoute, err := am.UpdateNameServerGroup(account.Id, testCase.nsGroupID, userID, testCase.operations) testCase.errFunc(t, err) if !testCase.shouldCreate { @@ -1009,7 +1010,7 @@ func TestDeleteNameServerGroup(t *testing.T) { t.Error("failed to save account") } - err = am.DeleteNameServerGroup(account.Id, testingNSGroup.ID) + err = am.DeleteNameServerGroup(account.Id, testingNSGroup.ID, userID) if err != nil { t.Error("deleting nameserver group failed with error: ", err) } diff --git a/management/server/peer.go b/management/server/peer.go index 43e9602da..021c98601 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -185,7 +185,7 @@ func (am *DefaultAccountManager) MarkPeerConnected(peerPubKey string, connected } // UpdatePeer updates peer. Only Peer.Name and Peer.SSHEnabled can be updated. -func (am *DefaultAccountManager) UpdatePeer(accountID string, update *Peer) (*Peer, error) { +func (am *DefaultAccountManager) UpdatePeer(accountID, userID string, update *Peer) (*Peer, error) { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -201,7 +201,14 @@ func (am *DefaultAccountManager) UpdatePeer(accountID string, update *Peer) (*Pe return nil, err } - peer.SSHEnabled = update.SSHEnabled + if peer.SSHEnabled != update.SSHEnabled { + peer.SSHEnabled = update.SSHEnabled + event := activity.PeerSSHEnabled + if !update.SSHEnabled { + event = activity.PeerSSHDisabled + } + am.storeEvent(userID, peer.IP.String(), accountID, event, peer.EventMeta(am.GetDNSDomain())) + } if peer.Name != update.Name { peer.Name = update.Name @@ -214,6 +221,8 @@ func (am *DefaultAccountManager) UpdatePeer(accountID string, update *Peer) (*Pe } peer.DNSLabel = newLabel + + am.storeEvent(userID, peer.IP.String(), accountID, activity.PeerRenamed, peer.EventMeta(am.GetDNSDomain())) } account.UpdatePeer(peer) diff --git a/management/server/route.go b/management/server/route.go index 36fd39c2d..d67cde83e 100644 --- a/management/server/route.go +++ b/management/server/route.go @@ -2,6 +2,7 @@ package server import ( "github.com/netbirdio/netbird/management/proto" + "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/status" "github.com/netbirdio/netbird/route" "github.com/rs/xid" @@ -118,7 +119,7 @@ func (am *DefaultAccountManager) checkPrefixPeerExists(accountID, peer string, p } // CreateRoute creates and saves a new route -func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) { +func (am *DefaultAccountManager) CreateRoute(accountID string, network, peerIP, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error) { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -127,23 +128,25 @@ func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, de return nil, err } + peerKey := "" + if peerIP != "" { + peer := account.GetPeerByIP(peerIP) + if peer == nil { + return nil, status.Errorf(status.NotFound, "peer %s not found", peerIP) + } + peerKey = peer.Key + } + var newRoute route.Route prefixType, newPrefix, err := route.ParseNetwork(network) if err != nil { return nil, status.Errorf(status.InvalidArgument, "failed to parse IP %s", network) } - err = am.checkPrefixPeerExists(accountID, peer, newPrefix) + err = am.checkPrefixPeerExists(accountID, peerKey, newPrefix) if err != nil { return nil, err } - if peer != "" { - _, peerExist := account.Peers[peer] - if !peerExist { - return nil, status.Errorf(status.InvalidArgument, "failed to find Peer %s", peer) - } - } - if metric < route.MinMetric || metric > route.MaxMetric { return nil, status.Errorf(status.InvalidArgument, "metric should be between %d and %d", route.MinMetric, route.MaxMetric) } @@ -157,7 +160,7 @@ func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, de return nil, err } - newRoute.Peer = peer + newRoute.Peer = peerKey newRoute.ID = xid.New().String() newRoute.Network = newPrefix newRoute.NetworkType = prefixType @@ -184,11 +187,14 @@ func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, de log.Error(err) return &newRoute, status.Errorf(status.Internal, "failed to update peers after create route %s", newPrefix) } + + am.storeEvent(userID, newRoute.ID, accountID, activity.RouteCreated, newRoute.EventMeta()) + return &newRoute, nil } // SaveRoute saves route -func (am *DefaultAccountManager) SaveRoute(accountID string, routeToSave *route.Route) error { +func (am *DefaultAccountManager) SaveRoute(accountID, userID string, routeToSave *route.Route) error { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -232,6 +238,8 @@ func (am *DefaultAccountManager) SaveRoute(accountID string, routeToSave *route. return err } + am.storeEvent(userID, routeToSave.ID, accountID, activity.RouteUpdated, routeToSave.EventMeta()) + return am.updateAccountPeers(account) } @@ -339,7 +347,7 @@ func (am *DefaultAccountManager) UpdateRoute(accountID, routeID string, operatio } // DeleteRoute deletes route with routeID -func (am *DefaultAccountManager) DeleteRoute(accountID, routeID string) error { +func (am *DefaultAccountManager) DeleteRoute(accountID, routeID, userID string) error { unlock := am.Store.AcquireAccountLock(accountID) defer unlock() @@ -348,6 +356,10 @@ func (am *DefaultAccountManager) DeleteRoute(accountID, routeID string) error { return err } + routy := account.Routes[routeID] + if routy == nil { + return status.Errorf(status.NotFound, "route with ID %s doesn't exist", routeID) + } delete(account.Routes, routeID) account.Network.IncSerial() @@ -355,6 +367,8 @@ func (am *DefaultAccountManager) DeleteRoute(accountID, routeID string) error { return err } + am.storeEvent(userID, routy.ID, accountID, activity.RouteRemoved, routy.EventMeta()) + return am.updateAccountPeers(account) } diff --git a/management/server/route_test.go b/management/server/route_test.go index 2d64c0cd7..39866492c 100644 --- a/management/server/route_test.go +++ b/management/server/route_test.go @@ -238,16 +238,25 @@ func TestCreateRoute(t *testing.T) { t.Error("failed to init testing account") } + peerIP := "99.99.99.99" + peer := account.Peers[testCase.inputArgs.peer] + if testCase.inputArgs.peer == "" { + peerIP = "" + } else if peer != nil { + peerIP = peer.IP.String() + } + outRoute, err := am.CreateRoute( account.Id, testCase.inputArgs.network, - testCase.inputArgs.peer, + peerIP, testCase.inputArgs.description, testCase.inputArgs.netID, testCase.inputArgs.masquerade, testCase.inputArgs.metric, testCase.inputArgs.groups, testCase.inputArgs.enabled, + userID, ) testCase.errFunc(t, err) @@ -500,7 +509,7 @@ func TestSaveRoute(t *testing.T) { } } - err = am.SaveRoute(account.Id, routeToSave) + err = am.SaveRoute(account.Id, userID, routeToSave) testCase.errFunc(t, err) @@ -814,7 +823,7 @@ func TestDeleteRoute(t *testing.T) { t.Error("failed to save account") } - err = am.DeleteRoute(account.Id, testingRoute.ID) + err = am.DeleteRoute(account.Id, testingRoute.ID, userID) if err != nil { t.Error("deleting route failed with error: ", err) } @@ -859,9 +868,11 @@ func TestGetNetworkMap_RouteSync(t *testing.T) { newAccountRoutes, err := am.GetNetworkMap(peer1Key) require.NoError(t, err) require.Len(t, newAccountRoutes.Routes, 0, "new accounts should have no routes") + peer := account.Peers[baseRoute.Peer] - createdRoute, err := am.CreateRoute(account.Id, baseRoute.Network.String(), baseRoute.Peer, - baseRoute.Description, baseRoute.NetID, baseRoute.Masquerade, baseRoute.Metric, baseRoute.Groups, false) + createdRoute, err := am.CreateRoute(account.Id, baseRoute.Network.String(), peer.IP.String(), + baseRoute.Description, baseRoute.NetID, baseRoute.Masquerade, baseRoute.Metric, baseRoute.Groups, false, + userID) require.NoError(t, err) noDisabledRoutes, err := am.GetNetworkMap(peer1Key) @@ -871,7 +882,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) { enabledRoute := createdRoute.Copy() enabledRoute.Enabled = true - err = am.SaveRoute(account.Id, enabledRoute) + err = am.SaveRoute(account.Id, userID, enabledRoute) require.NoError(t, err) peer1Routes, err := am.GetNetworkMap(peer1Key) @@ -923,7 +934,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) { require.NoError(t, err) require.Len(t, peer2GroupRoutes.Routes, 0, "we should not receive routes for peer2") - err = am.DeleteRoute(account.Id, enabledRoute.ID) + err = am.DeleteRoute(account.Id, enabledRoute.ID, userID) require.NoError(t, err) peer1DeletedRoute, err := am.GetNetworkMap(peer1Key) @@ -952,6 +963,7 @@ func createRouterStore(t *testing.T) (Store, error) { } func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*Account, error) { + peer1 := &Peer{ Key: peer1Key, Name: "test-host1@netbird.io", diff --git a/route/route.go b/route/route.go index eea996281..b4a68e03c 100644 --- a/route/route.go +++ b/route/route.go @@ -76,6 +76,11 @@ type Route struct { Groups []string } +// EventMeta returns activity event meta related to the route +func (r *Route) EventMeta() map[string]any { + return map[string]any{"name": r.NetID, "network_range": r.Network.String()} +} + // Copy copies a route object func (r *Route) Copy() *Route { return &Route{