Merge branch 'main' into refactor/permissions-manager

# Conflicts:
#	management/internals/modules/reverseproxy/service/manager/api.go
#	management/server/http/testing/testing_tools/channel/channel.go
This commit is contained in:
pascal
2026-03-27 14:37:29 +01:00
125 changed files with 11320 additions and 413 deletions

View File

@@ -127,14 +127,14 @@ func BuildApiBlackBoxWithDBState(t testing_tools.TB, sqlFile string, expectedPee
GetPATInfoFunc: authManager.GetPATInfo,
}
networksManagerMock := networks.NewManagerMock()
resourcesManagerMock := resources.NewManagerMock()
routersManagerMock := routers.NewManagerMock()
groupsManagerMock := groups.NewManagerMock()
groupsManager := groups.NewManager(store, permissionsManager, am)
routersManager := routers.NewManager(store, permissionsManager, am)
resourcesManager := resources.NewManager(store, permissionsManager, groupsManager, am, serviceManager)
networksManager := networks.NewManager(store, permissionsManager, resourcesManager, routersManager, am)
customZonesManager := zonesManager.NewManager(store, am, "")
zoneRecordsManager := recordsManager.NewManager(store, am)
apiHandler, err := http2.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager, customZonesManager, zoneRecordsManager, networkMapController, nil, serviceManager, nil, nil, nil, nil)
apiHandler, err := http2.NewAPIHandler(context.Background(), am, networksManager, resourcesManager, routersManager, groupsManager, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager, customZonesManager, zoneRecordsManager, networkMapController, nil, serviceManager, nil, nil, nil, nil)
if err != nil {
t.Fatalf("Failed to create API handler: %v", err)
}
@@ -166,6 +166,112 @@ func peerShouldReceiveUpdate(t testing_tools.TB, updateMessage <-chan *network_m
}
}
// PeerShouldReceiveAnyUpdate waits for a peer update message and returns it.
// Fails the test if no update is received within timeout.
func PeerShouldReceiveAnyUpdate(t testing_tools.TB, updateMessage <-chan *network_map.UpdateMessage) *network_map.UpdateMessage {
t.Helper()
select {
case msg := <-updateMessage:
if msg == nil {
t.Errorf("Received nil update message, expected valid message")
}
return msg
case <-time.After(500 * time.Millisecond):
t.Errorf("Timed out waiting for update message")
return nil
}
}
// PeerShouldNotReceiveAnyUpdate verifies no peer update message is received.
func PeerShouldNotReceiveAnyUpdate(t testing_tools.TB, updateMessage <-chan *network_map.UpdateMessage) {
t.Helper()
peerShouldNotReceiveUpdate(t, updateMessage)
}
// BuildApiBlackBoxWithDBStateAndPeerChannel creates the API handler and returns
// the peer update channel directly so tests can verify updates inline.
func BuildApiBlackBoxWithDBStateAndPeerChannel(t testing_tools.TB, sqlFile string) (http.Handler, account.Manager, <-chan *network_map.UpdateMessage) {
store, cleanup, err := store.NewTestStoreFromSQL(context.Background(), sqlFile, t.TempDir())
if err != nil {
t.Fatalf("Failed to create test store: %v", err)
}
t.Cleanup(cleanup)
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
if err != nil {
t.Fatalf("Failed to create metrics: %v", err)
}
peersUpdateManager := update_channel.NewPeersUpdateManager(nil)
updMsg := peersUpdateManager.CreateChannel(context.Background(), testing_tools.TestPeerId)
geoMock := &geolocation.Mock{}
validatorMock := server.MockIntegratedValidator{}
proxyController := integrations.NewController(store)
userManager := users.NewManager(store)
permissionsManager := permissions.NewManager(store)
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManager, settings.IdpConfig{})
peersManager := peers.NewManager(store, permissionsManager)
jobManager := job.NewJobManager(nil, store, peersManager)
ctx := context.Background()
requestBuffer := server.NewAccountRequestBuffer(ctx, store)
networkMapController := controller.NewController(ctx, store, metrics, peersUpdateManager, requestBuffer, server.MockIntegratedValidator{}, settingsManager, "", port_forwarding.NewControllerMock(), ephemeral_manager.NewEphemeralManager(store, peersManager), &config.Config{})
am, err := server.BuildManager(ctx, nil, store, networkMapController, jobManager, nil, "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManager, false)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
accessLogsManager := accesslogsmanager.NewManager(store, permissionsManager, nil)
proxyTokenStore, err := nbgrpc.NewOneTimeTokenStore(ctx, 5*time.Minute, 10*time.Minute, 100)
if err != nil {
t.Fatalf("Failed to create proxy token store: %v", err)
}
pkceverifierStore, err := nbgrpc.NewPKCEVerifierStore(ctx, 10*time.Minute, 10*time.Minute, 100)
if err != nil {
t.Fatalf("Failed to create PKCE verifier store: %v", err)
}
noopMeter := noop.NewMeterProvider().Meter("")
proxyMgr, err := proxymanager.NewManager(store, noopMeter)
if err != nil {
t.Fatalf("Failed to create proxy manager: %v", err)
}
proxyServiceServer := nbgrpc.NewProxyServiceServer(accessLogsManager, proxyTokenStore, pkceverifierStore, nbgrpc.ProxyOIDCConfig{}, peersManager, userManager, proxyMgr)
domainManager := manager.NewManager(store, proxyMgr, permissionsManager, am)
serviceProxyController, err := proxymanager.NewGRPCController(proxyServiceServer, noopMeter)
if err != nil {
t.Fatalf("Failed to create proxy controller: %v", err)
}
domainManager.SetClusterCapabilities(serviceProxyController)
serviceManager := reverseproxymanager.NewManager(store, am, permissionsManager, serviceProxyController, domainManager)
proxyServiceServer.SetServiceManager(serviceManager)
am.SetServiceManager(serviceManager)
// @note this is required so that PAT's validate from store, but JWT's are mocked
authManager := serverauth.NewManager(store, "", "", "", "", []string{}, false)
authManagerMock := &serverauth.MockManager{
ValidateAndParseTokenFunc: mockValidateAndParseToken,
EnsureUserAccessByJWTGroupsFunc: authManager.EnsureUserAccessByJWTGroups,
MarkPATUsedFunc: authManager.MarkPATUsed,
GetPATInfoFunc: authManager.GetPATInfo,
}
groupsManager := groups.NewManager(store, permissionsManager, am)
routersManager := routers.NewManager(store, permissionsManager, am)
resourcesManager := resources.NewManager(store, permissionsManager, groupsManager, am, serviceManager)
networksManager := networks.NewManager(store, permissionsManager, resourcesManager, routersManager, am)
customZonesManager := zonesManager.NewManager(store, am, permissionsManager, "")
zoneRecordsManager := recordsManager.NewManager(store, am, permissionsManager)
apiHandler, err := http2.NewAPIHandler(context.Background(), am, networksManager, resourcesManager, routersManager, groupsManager, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager, customZonesManager, zoneRecordsManager, networkMapController, nil, serviceManager, nil, nil, nil, nil)
if err != nil {
t.Fatalf("Failed to create API handler: %v", err)
}
return apiHandler, am, updMsg
}
func mockValidateAndParseToken(_ context.Context, token string) (auth.UserAuth, *jwt.Token, error) {
userAuth := auth.UserAuth{}

View File

@@ -0,0 +1,222 @@
package testing_tools
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
nbdns "github.com/netbirdio/netbird/dns"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/route"
)
// GetDB extracts the *gorm.DB from a store.Store (must be *SqlStore).
func GetDB(t *testing.T, s store.Store) *gorm.DB {
t.Helper()
sqlStore, ok := s.(*store.SqlStore)
require.True(t, ok, "Store is not a *SqlStore, cannot get gorm.DB")
return sqlStore.GetDB()
}
// VerifyGroupInDB reads a group directly from the DB and returns it.
func VerifyGroupInDB(t *testing.T, db *gorm.DB, groupID string) *types.Group {
t.Helper()
var group types.Group
err := db.Where("id = ? AND account_id = ?", groupID, TestAccountId).First(&group).Error
require.NoError(t, err, "Expected group %s to exist in DB", groupID)
return &group
}
// VerifyGroupNotInDB verifies that a group does not exist in the DB.
func VerifyGroupNotInDB(t *testing.T, db *gorm.DB, groupID string) {
t.Helper()
var count int64
db.Model(&types.Group{}).Where("id = ? AND account_id = ?", groupID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected group %s to NOT exist in DB", groupID)
}
// VerifyPolicyInDB reads a policy directly from the DB and returns it.
func VerifyPolicyInDB(t *testing.T, db *gorm.DB, policyID string) *types.Policy {
t.Helper()
var policy types.Policy
err := db.Preload("Rules").Where("id = ? AND account_id = ?", policyID, TestAccountId).First(&policy).Error
require.NoError(t, err, "Expected policy %s to exist in DB", policyID)
return &policy
}
// VerifyPolicyNotInDB verifies that a policy does not exist in the DB.
func VerifyPolicyNotInDB(t *testing.T, db *gorm.DB, policyID string) {
t.Helper()
var count int64
db.Model(&types.Policy{}).Where("id = ? AND account_id = ?", policyID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected policy %s to NOT exist in DB", policyID)
}
// VerifyRouteInDB reads a route directly from the DB and returns it.
func VerifyRouteInDB(t *testing.T, db *gorm.DB, routeID route.ID) *route.Route {
t.Helper()
var r route.Route
err := db.Where("id = ? AND account_id = ?", routeID, TestAccountId).First(&r).Error
require.NoError(t, err, "Expected route %s to exist in DB", routeID)
return &r
}
// VerifyRouteNotInDB verifies that a route does not exist in the DB.
func VerifyRouteNotInDB(t *testing.T, db *gorm.DB, routeID route.ID) {
t.Helper()
var count int64
db.Model(&route.Route{}).Where("id = ? AND account_id = ?", routeID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected route %s to NOT exist in DB", routeID)
}
// VerifyNSGroupInDB reads a nameserver group directly from the DB and returns it.
func VerifyNSGroupInDB(t *testing.T, db *gorm.DB, nsGroupID string) *nbdns.NameServerGroup {
t.Helper()
var nsGroup nbdns.NameServerGroup
err := db.Where("id = ? AND account_id = ?", nsGroupID, TestAccountId).First(&nsGroup).Error
require.NoError(t, err, "Expected NS group %s to exist in DB", nsGroupID)
return &nsGroup
}
// VerifyNSGroupNotInDB verifies that a nameserver group does not exist in the DB.
func VerifyNSGroupNotInDB(t *testing.T, db *gorm.DB, nsGroupID string) {
t.Helper()
var count int64
db.Model(&nbdns.NameServerGroup{}).Where("id = ? AND account_id = ?", nsGroupID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected NS group %s to NOT exist in DB", nsGroupID)
}
// VerifyPeerInDB reads a peer directly from the DB and returns it.
func VerifyPeerInDB(t *testing.T, db *gorm.DB, peerID string) *nbpeer.Peer {
t.Helper()
var peer nbpeer.Peer
err := db.Where("id = ? AND account_id = ?", peerID, TestAccountId).First(&peer).Error
require.NoError(t, err, "Expected peer %s to exist in DB", peerID)
return &peer
}
// VerifyPeerNotInDB verifies that a peer does not exist in the DB.
func VerifyPeerNotInDB(t *testing.T, db *gorm.DB, peerID string) {
t.Helper()
var count int64
db.Model(&nbpeer.Peer{}).Where("id = ? AND account_id = ?", peerID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected peer %s to NOT exist in DB", peerID)
}
// VerifySetupKeyInDB reads a setup key directly from the DB and returns it.
func VerifySetupKeyInDB(t *testing.T, db *gorm.DB, keyID string) *types.SetupKey {
t.Helper()
var key types.SetupKey
err := db.Where("id = ? AND account_id = ?", keyID, TestAccountId).First(&key).Error
require.NoError(t, err, "Expected setup key %s to exist in DB", keyID)
return &key
}
// VerifySetupKeyNotInDB verifies that a setup key does not exist in the DB.
func VerifySetupKeyNotInDB(t *testing.T, db *gorm.DB, keyID string) {
t.Helper()
var count int64
db.Model(&types.SetupKey{}).Where("id = ? AND account_id = ?", keyID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected setup key %s to NOT exist in DB", keyID)
}
// VerifyUserInDB reads a user directly from the DB and returns it.
func VerifyUserInDB(t *testing.T, db *gorm.DB, userID string) *types.User {
t.Helper()
var user types.User
err := db.Where("id = ? AND account_id = ?", userID, TestAccountId).First(&user).Error
require.NoError(t, err, "Expected user %s to exist in DB", userID)
return &user
}
// VerifyUserNotInDB verifies that a user does not exist in the DB.
func VerifyUserNotInDB(t *testing.T, db *gorm.DB, userID string) {
t.Helper()
var count int64
db.Model(&types.User{}).Where("id = ? AND account_id = ?", userID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected user %s to NOT exist in DB", userID)
}
// VerifyPATInDB reads a PAT directly from the DB and returns it.
func VerifyPATInDB(t *testing.T, db *gorm.DB, tokenID string) *types.PersonalAccessToken {
t.Helper()
var pat types.PersonalAccessToken
err := db.Where("id = ?", tokenID).First(&pat).Error
require.NoError(t, err, "Expected PAT %s to exist in DB", tokenID)
return &pat
}
// VerifyPATNotInDB verifies that a PAT does not exist in the DB.
func VerifyPATNotInDB(t *testing.T, db *gorm.DB, tokenID string) {
t.Helper()
var count int64
db.Model(&types.PersonalAccessToken{}).Where("id = ?", tokenID).Count(&count)
assert.Equal(t, int64(0), count, "Expected PAT %s to NOT exist in DB", tokenID)
}
// VerifyAccountSettings reads the account and returns its settings from the DB.
func VerifyAccountSettings(t *testing.T, db *gorm.DB) *types.Account {
t.Helper()
var account types.Account
err := db.Where("id = ?", TestAccountId).First(&account).Error
require.NoError(t, err, "Expected account %s to exist in DB", TestAccountId)
return &account
}
// VerifyNetworkInDB reads a network directly from the store and returns it.
func VerifyNetworkInDB(t *testing.T, db *gorm.DB, networkID string) *networkTypes.Network {
t.Helper()
var network networkTypes.Network
err := db.Where("id = ? AND account_id = ?", networkID, TestAccountId).First(&network).Error
require.NoError(t, err, "Expected network %s to exist in DB", networkID)
return &network
}
// VerifyNetworkNotInDB verifies that a network does not exist in the DB.
func VerifyNetworkNotInDB(t *testing.T, db *gorm.DB, networkID string) {
t.Helper()
var count int64
db.Model(&networkTypes.Network{}).Where("id = ? AND account_id = ?", networkID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected network %s to NOT exist in DB", networkID)
}
// VerifyNetworkResourceInDB reads a network resource directly from the DB and returns it.
func VerifyNetworkResourceInDB(t *testing.T, db *gorm.DB, resourceID string) *resourceTypes.NetworkResource {
t.Helper()
var resource resourceTypes.NetworkResource
err := db.Where("id = ? AND account_id = ?", resourceID, TestAccountId).First(&resource).Error
require.NoError(t, err, "Expected network resource %s to exist in DB", resourceID)
return &resource
}
// VerifyNetworkResourceNotInDB verifies that a network resource does not exist in the DB.
func VerifyNetworkResourceNotInDB(t *testing.T, db *gorm.DB, resourceID string) {
t.Helper()
var count int64
db.Model(&resourceTypes.NetworkResource{}).Where("id = ? AND account_id = ?", resourceID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected network resource %s to NOT exist in DB", resourceID)
}
// VerifyNetworkRouterInDB reads a network router directly from the DB and returns it.
func VerifyNetworkRouterInDB(t *testing.T, db *gorm.DB, routerID string) *routerTypes.NetworkRouter {
t.Helper()
var router routerTypes.NetworkRouter
err := db.Where("id = ? AND account_id = ?", routerID, TestAccountId).First(&router).Error
require.NoError(t, err, "Expected network router %s to exist in DB", routerID)
return &router
}
// VerifyNetworkRouterNotInDB verifies that a network router does not exist in the DB.
func VerifyNetworkRouterNotInDB(t *testing.T, db *gorm.DB, routerID string) {
t.Helper()
var count int64
db.Model(&routerTypes.NetworkRouter{}).Where("id = ? AND account_id = ?", routerID, TestAccountId).Count(&count)
assert.Equal(t, int64(0), count, "Expected network router %s to NOT exist in DB", routerID)
}