mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-03 03:59:07 +02:00
[management] expose peer MAC addresses and make peers searchable by MAC (#6553)
This commit is contained in:
@@ -44,7 +44,7 @@ func (r *repository) GetAccountNetwork(ctx context.Context, accountID string) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *repository) GetAccountPeers(ctx context.Context, accountID string) ([]*peer.Peer, error) {
|
func (r *repository) GetAccountPeers(ctx context.Context, accountID string) ([]*peer.Peer, error) {
|
||||||
return r.store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
return r.store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repository) GetAccountByPeerID(ctx context.Context, peerID string) (*types.Account, error) {
|
func (r *repository) GetAccountByPeerID(ctx context.Context, peerID string) (*types.Account, error) {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func (m *managerImpl) GetAllPeers(ctx context.Context, accountID, userID string)
|
|||||||
return m.store.GetUserPeers(ctx, store.LockingStrengthNone, accountID, userID)
|
return m.store.GetUserPeers(ctx, store.LockingStrengthNone, accountID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
return m.store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *managerImpl) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
|
func (m *managerImpl) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
|
||||||
|
|||||||
@@ -2391,7 +2391,7 @@ func (am *DefaultAccountManager) reallocateAccountPeerIPs(ctx context.Context, t
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthUpdate, accountID, "", "")
|
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthUpdate, accountID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -2428,7 +2428,7 @@ func (am *DefaultAccountManager) reallocateAccountPeerIPs(ctx context.Context, t
|
|||||||
// v6 address get one allocated. When disabled, all v6 addresses are cleared.
|
// v6 address get one allocated. When disabled, all v6 addresses are cleared.
|
||||||
// When the v6 range changes, all v6 addresses are reallocated.
|
// When the v6 range changes, all v6 addresses are reallocated.
|
||||||
func (am *DefaultAccountManager) checkIPv6Collision(ctx context.Context, transaction store.Store, accountID, peerID string, newIPv6 netip.Addr) error {
|
func (am *DefaultAccountManager) checkIPv6Collision(ctx context.Context, transaction store.Store, accountID, peerID string, newIPv6 netip.Addr) error {
|
||||||
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
|
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get peers: %w", err)
|
return fmt.Errorf("get peers: %w", err)
|
||||||
}
|
}
|
||||||
@@ -2441,7 +2441,7 @@ func (am *DefaultAccountManager) checkIPv6Collision(ctx context.Context, transac
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) updatePeerIPv6Addresses(ctx context.Context, transaction store.Store, accountID string, settings *types.Settings) error {
|
func (am *DefaultAccountManager) updatePeerIPv6Addresses(ctx context.Context, transaction store.Store, accountID string, settings *types.Settings) error {
|
||||||
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthUpdate, accountID, "", "")
|
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthUpdate, accountID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get peers: %w", err)
|
return fmt.Errorf("get peers: %w", err)
|
||||||
}
|
}
|
||||||
@@ -2602,7 +2602,7 @@ func (am *DefaultAccountManager) buildIPv6AllowedPeers(ctx context.Context, tran
|
|||||||
|
|
||||||
// Embedded proxy peers sit outside regular group membership but must
|
// Embedded proxy peers sit outside regular group membership but must
|
||||||
// participate in any v6-enabled overlay to reach v6-only peers.
|
// participate in any v6-enabled overlay to reach v6-only peers.
|
||||||
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get peers: %w", err)
|
return nil, fmt.Errorf("get peers: %w", err)
|
||||||
}
|
}
|
||||||
@@ -2673,7 +2673,7 @@ func (am *DefaultAccountManager) updatePeerIPInTransaction(ctx context.Context,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
|
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get account peers: %w", err)
|
return fmt.Errorf("get account peers: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ type Manager interface {
|
|||||||
GetUserByID(ctx context.Context, id string) (*types.User, error)
|
GetUserByID(ctx context.Context, id string) (*types.User, error)
|
||||||
GetUserFromUserAuth(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
GetUserFromUserAuth(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
||||||
ListUsers(ctx context.Context, accountID string) ([]*types.User, error)
|
ListUsers(ctx context.Context, accountID string) ([]*types.User, error)
|
||||||
GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
|
GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error)
|
||||||
MarkPeerConnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
MarkPeerConnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
||||||
MarkPeerDisconnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
MarkPeerDisconnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
||||||
DeletePeer(ctx context.Context, accountID, peerID, userID string) error
|
DeletePeer(ctx context.Context, accountID, peerID, userID string) error
|
||||||
|
|||||||
@@ -982,18 +982,18 @@ func (mr *MockManagerMockRecorder) GetPeerNetwork(ctx, peerID any) *gomock.Call
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPeers mocks base method.
|
// GetPeers mocks base method.
|
||||||
func (m *MockManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*peer.Peer, error) {
|
func (m *MockManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*peer.Peer, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetPeers", ctx, accountID, userID, nameFilter, ipFilter)
|
ret := m.ctrl.Call(m, "GetPeers", ctx, accountID, userID, nameFilter, ipFilter, macFilter)
|
||||||
ret0, _ := ret[0].([]*peer.Peer)
|
ret0, _ := ret[0].([]*peer.Peer)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeers indicates an expected call of GetPeers.
|
// GetPeers indicates an expected call of GetPeers.
|
||||||
func (mr *MockManagerMockRecorder) GetPeers(ctx, accountID, userID, nameFilter, ipFilter any) *gomock.Call {
|
func (mr *MockManagerMockRecorder) GetPeers(ctx, accountID, userID, nameFilter, ipFilter, macFilter any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeers", reflect.TypeOf((*MockManager)(nil).GetPeers), ctx, accountID, userID, nameFilter, ipFilter)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeers", reflect.TypeOf((*MockManager)(nil).GetPeers), ctx, accountID, userID, nameFilter, ipFilter, macFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPolicy mocks base method.
|
// GetPolicy mocks base method.
|
||||||
|
|||||||
@@ -2557,7 +2557,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerApproval(t *testing.T)
|
|||||||
_, err = manager.UpdateAccountSettings(ctx, accountID, userID, newSettings)
|
_, err = manager.UpdateAccountSettings(ctx, accountID, userID, newSettings)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
accountPeers, err := manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
accountPeers, err := manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, peer := range accountPeers {
|
for _, peer := range accountPeers {
|
||||||
@@ -4557,7 +4557,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_NetworkRangePreserved(t *te
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
peers, err := manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
peers, err := manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, peers, len(before))
|
require.Len(t, peers, len(before))
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
@@ -4575,7 +4575,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_NetworkRangePreserved(t *te
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
assert.Equal(t, before[p.ID], p.IP, "peer %s IP should not change for host-bit-set equivalent range", p.ID)
|
assert.Equal(t, before[p.ID], p.IP, "peer %s IP should not change for host-bit-set equivalent range", p.ID)
|
||||||
@@ -4589,7 +4589,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_NetworkRangePreserved(t *te
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
assert.Equal(t, before[p.ID], p.IP, "peer %s IP should not change when NetworkRange omitted", p.ID)
|
assert.Equal(t, before[p.ID], p.IP, "peer %s IP should not change when NetworkRange omitted", p.ID)
|
||||||
@@ -4605,7 +4605,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_NetworkRangePreserved(t *te
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
assert.True(t, newRange.Contains(p.IP), "peer %s should be in new range %s, got %s", p.ID, newRange, p.IP)
|
assert.True(t, newRange.Contains(p.IP), "peer %s should be in new range %s, got %s", p.ID, newRange, p.IP)
|
||||||
@@ -4623,7 +4623,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_IPv6EnabledGroups(t *testin
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, settings.IPv6EnabledGroups, "new account should have IPv6 enabled for All group")
|
require.NotEmpty(t, settings.IPv6EnabledGroups, "new account should have IPv6 enabled for All group")
|
||||||
|
|
||||||
peers, err := manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
peers, err := manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
assert.True(t, p.IPv6.IsValid(), "peer %s should have IPv6 with All group enabled", p.ID)
|
assert.True(t, p.IPv6.IsValid(), "peer %s should have IPv6 with All group enabled", p.ID)
|
||||||
@@ -4651,7 +4651,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_IPv6EnabledGroups(t *testin
|
|||||||
assert.Equal(t, []string{partialGroup.ID}, updatedSettings.IPv6EnabledGroups)
|
assert.Equal(t, []string{partialGroup.ID}, updatedSettings.IPv6EnabledGroups)
|
||||||
|
|
||||||
// peer1 and peer2 should have IPv6; peer3 should not.
|
// peer1 and peer2 should have IPv6; peer3 should not.
|
||||||
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
peerMap := make(map[string]*nbpeer.Peer, len(peers))
|
peerMap := make(map[string]*nbpeer.Peer, len(peers))
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
@@ -4671,7 +4671,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_IPv6EnabledGroups(t *testin
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Empty(t, updatedSettings.IPv6EnabledGroups)
|
assert.Empty(t, updatedSettings.IPv6EnabledGroups)
|
||||||
|
|
||||||
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
assert.False(t, p.IPv6.IsValid(), "peer %s should have no IPv6 when groups cleared", p.ID)
|
assert.False(t, p.IPv6.IsValid(), "peer %s should have no IPv6 when groups cleared", p.ID)
|
||||||
@@ -4686,7 +4686,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_IPv6EnabledGroups(t *testin
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
peerMap = make(map[string]*nbpeer.Peer, len(peers))
|
peerMap = make(map[string]*nbpeer.Peer, len(peers))
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ func (h *handler) validateNetworkRange(ctx context.Context, accountID, userID st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) validateCapacity(ctx context.Context, accountID, userID string, prefix netip.Prefix) error {
|
func (h *handler) validateCapacity(ctx context.Context, accountID, userID string, prefix netip.Prefix) error {
|
||||||
peers, err := h.accountManager.GetPeers(ctx, accountID, userID, "", "")
|
peers, err := h.accountManager.GetPeers(ctx, accountID, userID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status.Errorf(status.Internal, "get peer count: %v", err)
|
return status.Errorf(status.Internal, "get peer count: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func (h *handler) getAllGroups(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
|
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
@@ -77,7 +77,7 @@ func (h *handler) getAllGroups(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
|
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
@@ -169,7 +169,7 @@ func (h *handler) updateGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
|
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
@@ -226,7 +226,7 @@ func (h *handler) createGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
|
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
@@ -287,7 +287,7 @@ func (h *handler) getGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
|
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func initGroupTestData(initGroups ...*types.Group) *handler {
|
|||||||
|
|
||||||
return nil, status.Errorf(status.NotFound, "unknown group name")
|
return nil, status.Errorf(status.NotFound, "unknown group name")
|
||||||
},
|
},
|
||||||
GetPeersFunc: func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
|
GetPeersFunc: func(ctx context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error) {
|
||||||
return maps.Values(TestPeers), nil
|
return maps.Values(TestPeers), nil
|
||||||
},
|
},
|
||||||
DeleteGroupFunc: func(_ context.Context, accountID, userId, groupID string) error {
|
DeleteGroupFunc: func(_ context.Context, accountID, userId, groupID string) error {
|
||||||
|
|||||||
@@ -317,10 +317,11 @@ func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
nameFilter := r.URL.Query().Get("name")
|
nameFilter := r.URL.Query().Get("name")
|
||||||
ipFilter := r.URL.Query().Get("ip")
|
ipFilter := r.URL.Query().Get("ip")
|
||||||
|
macFilter := r.URL.Query().Get("mac")
|
||||||
|
|
||||||
accountID, userID := userAuth.AccountId, userAuth.UserId
|
accountID, userID := userAuth.AccountId, userAuth.UserId
|
||||||
|
|
||||||
peers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, nameFilter, ipFilter)
|
peers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, nameFilter, ipFilter, macFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
@@ -571,6 +572,17 @@ func peerToAccessiblePeer(peer *nbpeer.Peer, dnsDomain string) api.AccessiblePee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toNetworkAddresses(addrs []nbpeer.NetworkAddress) *[]api.NetworkAddress {
|
||||||
|
if len(addrs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]api.NetworkAddress, 0, len(addrs))
|
||||||
|
for _, a := range addrs {
|
||||||
|
out = append(out, api.NetworkAddress{NetIp: a.NetIP.String(), Mac: a.Mac})
|
||||||
|
}
|
||||||
|
return &out
|
||||||
|
}
|
||||||
|
|
||||||
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, approved bool, reason string) *api.Peer {
|
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, approved bool, reason string) *api.Peer {
|
||||||
osVersion := peer.Meta.OSVersion
|
osVersion := peer.Meta.OSVersion
|
||||||
if osVersion == "" {
|
if osVersion == "" {
|
||||||
@@ -583,6 +595,7 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD
|
|||||||
Name: peer.Name,
|
Name: peer.Name,
|
||||||
Ip: peer.IP.String(),
|
Ip: peer.IP.String(),
|
||||||
Ipv6: peerIPv6String(peer),
|
Ipv6: peerIPv6String(peer),
|
||||||
|
NetworkAddresses: toNetworkAddresses(peer.Meta.NetworkAddresses),
|
||||||
ConnectionIp: peer.Location.ConnectionIP.String(),
|
ConnectionIp: peer.Location.ConnectionIP.String(),
|
||||||
Connected: peer.Status.Connected,
|
Connected: peer.Status.Connected,
|
||||||
LastSeen: peer.Status.LastSeen,
|
LastSeen: peer.Status.LastSeen,
|
||||||
@@ -639,6 +652,7 @@ func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dn
|
|||||||
Name: peer.Name,
|
Name: peer.Name,
|
||||||
Ip: peer.IP.String(),
|
Ip: peer.IP.String(),
|
||||||
Ipv6: peerIPv6String(peer),
|
Ipv6: peerIPv6String(peer),
|
||||||
|
NetworkAddresses: toNetworkAddresses(peer.Meta.NetworkAddresses),
|
||||||
ConnectionIp: peer.Location.ConnectionIP.String(),
|
ConnectionIp: peer.Location.ConnectionIP.String(),
|
||||||
Connected: peer.Status.Connected,
|
Connected: peer.Status.Connected,
|
||||||
LastSeen: peer.Status.LastSeen,
|
LastSeen: peer.Status.LastSeen,
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ func initTestMetaData(t *testing.T, peers ...*nbpeer.Peer) *Handler {
|
|||||||
return nil, fmt.Errorf("user not found")
|
return nil, fmt.Errorf("user not found")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GetPeersFunc: func(_ context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
|
GetPeersFunc: func(_ context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error) {
|
||||||
return peers, nil
|
return peers, nil
|
||||||
},
|
},
|
||||||
GetPeerGroupsFunc: func(ctx context.Context, accountID, peerID string) ([]*types.Group, error) {
|
GetPeerGroupsFunc: func(ctx context.Context, accountID, peerID string) ([]*types.Group, error) {
|
||||||
@@ -364,6 +364,50 @@ func TestGetPeers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPeerResponseNetworkAddresses(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
addresses []nbpeer.NetworkAddress
|
||||||
|
wantJSON string
|
||||||
|
}{
|
||||||
|
{name: "not reported"},
|
||||||
|
{name: "empty", addresses: []nbpeer.NetworkAddress{}},
|
||||||
|
{
|
||||||
|
name: "multiple interfaces",
|
||||||
|
addresses: []nbpeer.NetworkAddress{
|
||||||
|
{NetIP: netip.MustParsePrefix("192.168.0.11/24"), Mac: "00:93:37:bd:83:0f"},
|
||||||
|
{NetIP: netip.MustParsePrefix("2001:db8::123/64"), Mac: "00:93:37:bd:83:10"},
|
||||||
|
},
|
||||||
|
wantJSON: `[{"net_ip":"192.168.0.11/24","mac":"00:93:37:bd:83:0f"},{"net_ip":"2001:db8::123/64","mac":"00:93:37:bd:83:10"}]`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
peer := &nbpeer.Peer{
|
||||||
|
Status: &nbpeer.PeerStatus{},
|
||||||
|
Meta: nbpeer.PeerSystemMeta{NetworkAddresses: tt.addresses},
|
||||||
|
}
|
||||||
|
responses := map[string]any{
|
||||||
|
"single peer": toSinglePeerResponse(peer, nil, "example.com", true, ""),
|
||||||
|
"peer list": toPeerListItemResponse(peer, nil, "example.com", 0),
|
||||||
|
}
|
||||||
|
for name, response := range responses {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
body, err := json.Marshal(response)
|
||||||
|
require.NoError(t, err)
|
||||||
|
var fields map[string]json.RawMessage
|
||||||
|
require.NoError(t, json.Unmarshal(body, &fields))
|
||||||
|
if tt.wantJSON == "" {
|
||||||
|
assert.NotContains(t, fields, "network_addresses", "unreported interfaces should be omitted")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.JSONEq(t, tt.wantJSON, string(fields["network_addresses"]), "response should preserve interface addresses and MACs")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetAccessiblePeers(t *testing.T) {
|
func TestGetAccessiblePeers(t *testing.T) {
|
||||||
peer1 := &nbpeer.Peer{
|
peer1 := &nbpeer.Peer{
|
||||||
ID: "peer1",
|
ID: "peer1",
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func (am *DefaultAccountManager) GetValidatedPeers(ctx context.Context, accountI
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peers, err = am.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
peers, err = am.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ type MockAccountManager struct {
|
|||||||
GetAccountIDByUserIdFunc func(ctx context.Context, userAuth auth.UserAuth) (string, error)
|
GetAccountIDByUserIdFunc func(ctx context.Context, userAuth auth.UserAuth) (string, error)
|
||||||
GetUserFromUserAuthFunc func(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
GetUserFromUserAuthFunc func(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
||||||
ListUsersFunc func(ctx context.Context, accountID string) ([]*types.User, error)
|
ListUsersFunc func(ctx context.Context, accountID string) ([]*types.User, error)
|
||||||
GetPeersFunc func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
|
GetPeersFunc func(ctx context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error)
|
||||||
MarkPeerConnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
MarkPeerConnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
||||||
MarkPeerDisconnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
MarkPeerDisconnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
||||||
SyncAndMarkPeerFunc func(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*nmdata.PostureChecks, int64, error)
|
SyncAndMarkPeerFunc func(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*nmdata.PostureChecks, int64, error)
|
||||||
@@ -807,9 +807,9 @@ func (am *MockAccountManager) GetAccountIDFromUserAuth(ctx context.Context, user
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPeers mocks GetPeers of the AccountManager interface
|
// GetPeers mocks GetPeers of the AccountManager interface
|
||||||
func (am *MockAccountManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
|
func (am *MockAccountManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error) {
|
||||||
if am.GetPeersFunc != nil {
|
if am.GetPeersFunc != nil {
|
||||||
return am.GetPeersFunc(ctx, accountID, userID, nameFilter, ipFilter)
|
return am.GetPeersFunc(ctx, accountID, userID, nameFilter, ipFilter, macFilter)
|
||||||
}
|
}
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetPeers is not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetPeers is not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const (
|
|||||||
|
|
||||||
// GetPeers returns peers visible to the user within an account.
|
// GetPeers returns peers visible to the user within an account.
|
||||||
// Users with "peers:read" see all peers. Otherwise, users see only their own peers, or none if restricted by account settings.
|
// Users with "peers:read" see all peers. Otherwise, users see only their own peers, or none if restricted by account settings.
|
||||||
func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
|
func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error) {
|
||||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthNone, userID)
|
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthNone, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -59,7 +59,7 @@ func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID
|
|||||||
}
|
}
|
||||||
|
|
||||||
if allowed {
|
if allowed {
|
||||||
return am.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, nameFilter, ipFilter)
|
return am.Store.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, nameFilter, ipFilter, macFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthNone, accountID)
|
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthNone, accountID)
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
b64 "encoding/base64"
|
b64 "encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -33,12 +37,15 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||||
"github.com/netbirdio/netbird/management/internals/shared/grpc"
|
"github.com/netbirdio/netbird/management/internals/shared/grpc"
|
||||||
nbcache "github.com/netbirdio/netbird/management/server/cache"
|
nbcache "github.com/netbirdio/netbird/management/server/cache"
|
||||||
|
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||||
|
peershandler "github.com/netbirdio/netbird/management/server/http/handlers/peers"
|
||||||
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools"
|
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools"
|
||||||
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
||||||
"github.com/netbirdio/netbird/management/server/job"
|
"github.com/netbirdio/netbird/management/server/job"
|
||||||
"github.com/netbirdio/netbird/management/server/permissions"
|
"github.com/netbirdio/netbird/management/server/permissions"
|
||||||
"github.com/netbirdio/netbird/management/server/settings"
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
"github.com/netbirdio/netbird/shared/auth"
|
"github.com/netbirdio/netbird/shared/auth"
|
||||||
|
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||||
"github.com/netbirdio/netbird/shared/management/status"
|
"github.com/netbirdio/netbird/shared/management/status"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/util"
|
"github.com/netbirdio/netbird/management/server/util"
|
||||||
@@ -718,7 +725,7 @@ func TestDefaultAccountManager_GetPeers(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
peers, err := manager.GetPeers(context.Background(), accountID, someUser, "", "")
|
peers, err := manager.GetPeers(context.Background(), accountID, someUser, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
@@ -731,6 +738,71 @@ func TestDefaultAccountManager_GetPeers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultAccountManager_GetPeers_FilterByMac(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
manager, _, err := createManager(t)
|
||||||
|
require.NoError(t, err)
|
||||||
|
account := newAccountWithId(ctx, "mac-account", "mac-admin", "", "", "", false)
|
||||||
|
account.Peers["matching"] = &nbpeer.Peer{
|
||||||
|
ID: "matching", Key: "matching-key", Name: "laptop", DNSLabel: "laptop",
|
||||||
|
IP: netip.MustParseAddr("100.64.0.10"), Status: &nbpeer.PeerStatus{LastSeen: time.Now()},
|
||||||
|
Meta: nbpeer.PeerSystemMeta{NetworkAddresses: []nbpeer.NetworkAddress{
|
||||||
|
{NetIP: netip.MustParsePrefix("192.168.0.11/24"), Mac: "00:93:37:bd:83:0f"},
|
||||||
|
{NetIP: netip.MustParsePrefix("192.168.1.11/24"), Mac: "aa:bb:cc:dd:ee:ff"},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
account.Peers["other"] = &nbpeer.Peer{
|
||||||
|
ID: "other", Key: "other-key", Name: "desktop", DNSLabel: "desktop",
|
||||||
|
IP: netip.MustParseAddr("100.64.0.20"), Status: &nbpeer.PeerStatus{LastSeen: time.Now()},
|
||||||
|
}
|
||||||
|
require.NoError(t, manager.Store.SaveAccount(ctx, account))
|
||||||
|
otherAccount := newAccountWithId(ctx, "other-account", "other-admin", "", "", "", false)
|
||||||
|
otherPeer := account.Peers["matching"].Copy()
|
||||||
|
otherPeer.ID, otherPeer.Key = "outside-account", "outside-key"
|
||||||
|
otherAccount.Peers[otherPeer.ID] = otherPeer
|
||||||
|
require.NoError(t, manager.Store.SaveAccount(ctx, otherAccount))
|
||||||
|
handler := peershandler.NewHandler(manager, manager.networkMapController, manager.permissionsManager)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name, nameFilter, ipFilter, macFilter string
|
||||||
|
wantIDs []string
|
||||||
|
}{
|
||||||
|
{name: "no filter", wantIDs: []string{"matching", "other"}},
|
||||||
|
{name: "full MAC", macFilter: "00:93:37:bd:83:0f", wantIDs: []string{"matching"}},
|
||||||
|
{name: "partial MAC", macFilter: "93:37:bd", wantIDs: []string{"matching"}},
|
||||||
|
{name: "second interface", macFilter: "aa:bb:cc:dd:ee:ff", wantIDs: []string{"matching"}},
|
||||||
|
{name: "unknown MAC", macFilter: "11:22:33:44:55:66"},
|
||||||
|
{name: "combined filters", nameFilter: "laptop", ipFilter: "100.64.0.10", macFilter: "00:93:37", wantIDs: []string{"matching"}},
|
||||||
|
{name: "name mismatch", nameFilter: "desktop", macFilter: "00:93:37"},
|
||||||
|
{name: "IP mismatch", ipFilter: "100.64.0.20", macFilter: "00:93:37"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
peers, err := manager.GetPeers(ctx, account.Id, "mac-admin", tt.nameFilter, tt.ipFilter, tt.macFilter)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ids := make([]string, 0, len(peers))
|
||||||
|
for _, peer := range peers {
|
||||||
|
ids = append(ids, peer.ID)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, tt.wantIDs, ids, "filters should return only matching peers in the account")
|
||||||
|
|
||||||
|
query := url.Values{"name": {tt.nameFilter}, "ip": {tt.ipFilter}, "mac": {tt.macFilter}}
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/api/peers?"+query.Encode(), nil)
|
||||||
|
req = nbcontext.SetUserAuthInRequest(req, auth.UserAuth{AccountId: account.Id, UserId: "mac-admin"})
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
handler.GetAllPeers(recorder, req)
|
||||||
|
require.Equal(t, http.StatusOK, recorder.Code, "peer listing should succeed: %s", recorder.Body.String())
|
||||||
|
var response []api.PeerBatch
|
||||||
|
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &response))
|
||||||
|
responseIDs := make([]string, 0, len(response))
|
||||||
|
for _, peer := range response {
|
||||||
|
responseIDs = append(responseIDs, peer.Id)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, tt.wantIDs, responseIDs, "HTTP query filters should reach the store")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setupTestAccountManager(b testing.TB, peers int, groups int) (*DefaultAccountManager, *update_channel.PeersUpdateManager, string, string, error) {
|
func setupTestAccountManager(b testing.TB, peers int, groups int) (*DefaultAccountManager, *update_channel.PeersUpdateManager, string, string, error) {
|
||||||
b.Helper()
|
b.Helper()
|
||||||
|
|
||||||
@@ -934,7 +1006,7 @@ func BenchmarkGetPeers(b *testing.B) {
|
|||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := manager.GetPeers(context.Background(), accountID, userID, "", "")
|
_, err := manager.GetPeers(context.Background(), accountID, userID, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("GetPeers failed: %v", err)
|
b.Fatalf("GetPeers failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ func (s *SqlStore) GetPeerByPeerPubKey(ctx context.Context, lockStrength Locking
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountPeers retrieves peers for an account.
|
// GetAccountPeers retrieves peers for an account.
|
||||||
func (s *SqlStore) GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
|
func (s *SqlStore) GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error) {
|
||||||
var peers []*nbpeer.Peer
|
var peers []*nbpeer.Peer
|
||||||
tx := s.db
|
tx := s.db
|
||||||
if lockStrength != LockingStrengthNone {
|
if lockStrength != LockingStrengthNone {
|
||||||
@@ -506,6 +506,11 @@ func (s *SqlStore) GetAccountPeers(ctx context.Context, lockStrength LockingStre
|
|||||||
if ipFilter != "" {
|
if ipFilter != "" {
|
||||||
query = query.Where("ip LIKE ? OR ipv6 LIKE ?", "%"+ipFilter+"%", "%"+ipFilter+"%")
|
query = query.Where("ip LIKE ? OR ipv6 LIKE ?", "%"+ipFilter+"%", "%"+ipFilter+"%")
|
||||||
}
|
}
|
||||||
|
// MAC addresses live in the JSON-serialized meta_network_addresses column,
|
||||||
|
// so we match the raw JSON text rather than a dedicated column.
|
||||||
|
if macFilter != "" {
|
||||||
|
query = query.Where("meta_network_addresses LIKE ?", "%"+macFilter+"%")
|
||||||
|
}
|
||||||
|
|
||||||
if err := query.Find(&peers).Error; err != nil {
|
if err := query.Find(&peers).Error; err != nil {
|
||||||
log.WithContext(ctx).Errorf("failed to get peers from the store: %s", err)
|
log.WithContext(ctx).Errorf("failed to get peers from the store: %s", err)
|
||||||
|
|||||||
@@ -512,7 +512,7 @@ func TestSqlStore_GetAccountPeers(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
peers, err := store.GetAccountPeers(context.Background(), LockingStrengthNone, tt.accountID, tt.nameFilter, tt.ipFilter)
|
peers, err := store.GetAccountPeers(context.Background(), LockingStrengthNone, tt.accountID, tt.nameFilter, tt.ipFilter, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, peers, tt.expectedCount)
|
require.Len(t, peers, tt.expectedCount)
|
||||||
})
|
})
|
||||||
@@ -520,6 +520,48 @@ func TestSqlStore_GetAccountPeers(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSqlStore_GetAccountPeers_FilterByMac(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
store, cleanup, err := NewTestStoreFromSQL(ctx, "", t.TempDir())
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(cleanup)
|
||||||
|
|
||||||
|
accountID := "test-account-mac"
|
||||||
|
userID := "test-user-mac"
|
||||||
|
account := newAccountWithId(ctx, accountID, userID, "example.com")
|
||||||
|
account.Peers["peer-mac-1"] = &nbpeer.Peer{
|
||||||
|
ID: "peer-mac-1",
|
||||||
|
AccountID: accountID,
|
||||||
|
Key: "peer-mac-key-1",
|
||||||
|
Name: "macpeer",
|
||||||
|
IP: netip.MustParseAddr("100.64.0.10"),
|
||||||
|
Meta: nbpeer.PeerSystemMeta{
|
||||||
|
NetworkAddresses: []nbpeer.NetworkAddress{
|
||||||
|
{NetIP: netip.MustParsePrefix("192.168.0.11/24"), Mac: "00:93:37:bd:83:0f"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, store.SaveAccount(ctx, account))
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
macFilter string
|
||||||
|
expectedCount int
|
||||||
|
}{
|
||||||
|
{name: "full mac matches", macFilter: "00:93:37:bd:83:0f", expectedCount: 1},
|
||||||
|
{name: "mac prefix matches", macFilter: "00:93:37", expectedCount: 1},
|
||||||
|
{name: "unknown mac does not match", macFilter: "11:22:33:44:55:66", expectedCount: 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
peers, err := store.GetAccountPeers(ctx, LockingStrengthNone, accountID, "", "", tt.macFilter)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, peers, tt.expectedCount)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSqlStore_GetAccountPeersWithExpiration(t *testing.T) {
|
func TestSqlStore_GetAccountPeersWithExpiration(t *testing.T) {
|
||||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store_with_expired_peers.sql", t.TempDir())
|
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store_with_expired_peers.sql", t.TempDir())
|
||||||
t.Cleanup(cleanup)
|
t.Cleanup(cleanup)
|
||||||
@@ -878,7 +920,7 @@ func TestSqlStore_ApproveAccountPeers(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, 2, count)
|
assert.Equal(t, 2, count)
|
||||||
|
|
||||||
allPeers, err := store.GetAccountPeers(ctx, LockingStrengthNone, accountID, "", "")
|
allPeers, err := store.GetAccountPeers(ctx, LockingStrengthNone, accountID, "", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, peer := range allPeers {
|
for _, peer := range allPeers {
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ type Store interface {
|
|||||||
RemoveResourceFromGroup(ctx context.Context, accountId string, groupID string, resourceID string) error
|
RemoveResourceFromGroup(ctx context.Context, accountId string, groupID string, resourceID string) error
|
||||||
AddPeerToAccount(ctx context.Context, peer *nbpeer.Peer) error
|
AddPeerToAccount(ctx context.Context, peer *nbpeer.Peer) error
|
||||||
GetPeerByPeerPubKey(ctx context.Context, lockStrength LockingStrength, peerKey string) (*nbpeer.Peer, error)
|
GetPeerByPeerPubKey(ctx context.Context, lockStrength LockingStrength, peerKey string) (*nbpeer.Peer, error)
|
||||||
GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
|
GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error)
|
||||||
GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID string) ([]*nbpeer.Peer, error)
|
GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID string) ([]*nbpeer.Peer, error)
|
||||||
GetPeerByID(ctx context.Context, lockStrength LockingStrength, accountID string, peerID string) (*nbpeer.Peer, error)
|
GetPeerByID(ctx context.Context, lockStrength LockingStrength, accountID string, peerID string) (*nbpeer.Peer, error)
|
||||||
GetPeersByIDs(ctx context.Context, lockStrength LockingStrength, accountID string, peerIDs []string) (map[string]*nbpeer.Peer, error)
|
GetPeersByIDs(ctx context.Context, lockStrength LockingStrength, accountID string, peerIDs []string) (map[string]*nbpeer.Peer, error)
|
||||||
|
|||||||
@@ -1330,18 +1330,18 @@ func (mr *MockStoreMockRecorder) GetAccountOwner(ctx, lockStrength, accountID an
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountPeers mocks base method.
|
// GetAccountPeers mocks base method.
|
||||||
func (m *MockStore) GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter string) ([]*peer.Peer, error) {
|
func (m *MockStore) GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter, macFilter string) ([]*peer.Peer, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetAccountPeers", ctx, lockStrength, accountID, nameFilter, ipFilter)
|
ret := m.ctrl.Call(m, "GetAccountPeers", ctx, lockStrength, accountID, nameFilter, ipFilter, macFilter)
|
||||||
ret0, _ := ret[0].([]*peer.Peer)
|
ret0, _ := ret[0].([]*peer.Peer)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountPeers indicates an expected call of GetAccountPeers.
|
// GetAccountPeers indicates an expected call of GetAccountPeers.
|
||||||
func (mr *MockStoreMockRecorder) GetAccountPeers(ctx, lockStrength, accountID, nameFilter, ipFilter any) *gomock.Call {
|
func (mr *MockStoreMockRecorder) GetAccountPeers(ctx, lockStrength, accountID, nameFilter, ipFilter, macFilter any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountPeers", reflect.TypeOf((*MockStore)(nil).GetAccountPeers), ctx, lockStrength, accountID, nameFilter, ipFilter)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountPeers", reflect.TypeOf((*MockStore)(nil).GetAccountPeers), ctx, lockStrength, accountID, nameFilter, ipFilter, macFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountPeersWithExpiration mocks base method.
|
// GetAccountPeersWithExpiration mocks base method.
|
||||||
|
|||||||
@@ -826,6 +826,20 @@ components:
|
|||||||
- ssh_enabled
|
- ssh_enabled
|
||||||
- login_expiration_enabled
|
- login_expiration_enabled
|
||||||
- inactivity_expiration_enabled
|
- inactivity_expiration_enabled
|
||||||
|
NetworkAddress:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
net_ip:
|
||||||
|
description: IP address with CIDR of the interface
|
||||||
|
type: string
|
||||||
|
example: 192.168.0.11/24
|
||||||
|
mac:
|
||||||
|
description: MAC address of the interface
|
||||||
|
type: string
|
||||||
|
example: "00:93:37:bd:83:0f"
|
||||||
|
required:
|
||||||
|
- net_ip
|
||||||
|
- mac
|
||||||
Peer:
|
Peer:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/PeerMinimum'
|
- $ref: '#/components/schemas/PeerMinimum'
|
||||||
@@ -845,6 +859,11 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
format: ipv6
|
format: ipv6
|
||||||
example: "fd00:4e42:ab12::1"
|
example: "fd00:4e42:ab12::1"
|
||||||
|
network_addresses:
|
||||||
|
description: Network interfaces (IP + MAC) reported by the peer
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/NetworkAddress'
|
||||||
connection_ip:
|
connection_ip:
|
||||||
description: Peer's public connection IP address
|
description: Peer's public connection IP address
|
||||||
type: string
|
type: string
|
||||||
@@ -7516,6 +7535,11 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
description: Filter peers by IP address
|
description: Filter peers by IP address
|
||||||
|
- in: query
|
||||||
|
name: mac
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Filter peers by MAC address of a network interface
|
||||||
security:
|
security:
|
||||||
- BearerAuth: [ ]
|
- BearerAuth: [ ]
|
||||||
- TokenAuth: [ ]
|
- TokenAuth: [ ]
|
||||||
|
|||||||
@@ -3829,6 +3829,15 @@ type Network struct {
|
|||||||
RoutingPeersCount int `json:"routing_peers_count"`
|
RoutingPeersCount int `json:"routing_peers_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NetworkAddress defines model for NetworkAddress.
|
||||||
|
type NetworkAddress struct {
|
||||||
|
// Mac MAC address of the interface
|
||||||
|
Mac string `json:"mac"`
|
||||||
|
|
||||||
|
// NetIp IP address with CIDR of the interface
|
||||||
|
NetIp string `json:"net_ip"`
|
||||||
|
}
|
||||||
|
|
||||||
// NetworkRequest defines model for NetworkRequest.
|
// NetworkRequest defines model for NetworkRequest.
|
||||||
type NetworkRequest struct {
|
type NetworkRequest struct {
|
||||||
// Description Network description
|
// Description Network description
|
||||||
@@ -4278,6 +4287,9 @@ type Peer struct {
|
|||||||
// Name Peer's hostname
|
// Name Peer's hostname
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// NetworkAddresses Network interfaces (IP + MAC) reported by the peer
|
||||||
|
NetworkAddresses *[]NetworkAddress `json:"network_addresses,omitempty"`
|
||||||
|
|
||||||
// Os Peer's operating system and version
|
// Os Peer's operating system and version
|
||||||
Os string `json:"os"`
|
Os string `json:"os"`
|
||||||
|
|
||||||
@@ -4372,6 +4384,9 @@ type PeerBatch struct {
|
|||||||
// Name Peer's hostname
|
// Name Peer's hostname
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// NetworkAddresses Network interfaces (IP + MAC) reported by the peer
|
||||||
|
NetworkAddresses *[]NetworkAddress `json:"network_addresses,omitempty"`
|
||||||
|
|
||||||
// Os Peer's operating system and version
|
// Os Peer's operating system and version
|
||||||
Os string `json:"os"`
|
Os string `json:"os"`
|
||||||
|
|
||||||
@@ -6294,6 +6309,9 @@ type GetApiPeersParams struct {
|
|||||||
|
|
||||||
// Ip Filter peers by IP address
|
// Ip Filter peers by IP address
|
||||||
Ip *string `form:"ip,omitempty" json:"ip,omitempty"`
|
Ip *string `form:"ip,omitempty" json:"ip,omitempty"`
|
||||||
|
|
||||||
|
// Mac Filter peers by MAC address of a network interface
|
||||||
|
Mac *string `form:"mac,omitempty" json:"mac,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetApiPeersPeerIdIngressPortsParams defines parameters for GetApiPeersPeerIdIngressPorts.
|
// GetApiPeersPeerIdIngressPortsParams defines parameters for GetApiPeersPeerIdIngressPorts.
|
||||||
|
|||||||
Reference in New Issue
Block a user