Merge remote-tracking branch 'origin/main' into refactor/permissions-manager

# Conflicts:
#	management/internals/modules/reverseproxy/domain/manager/manager.go
#	management/internals/modules/reverseproxy/service/manager/api.go
#	management/internals/server/modules.go
#	management/server/http/testing/testing_tools/channel/channel.go
This commit is contained in:
pascal
2026-03-17 12:38:08 +01:00
244 changed files with 17304 additions and 3509 deletions

View File

@@ -175,7 +175,6 @@ func NewAPIHandler(ctx context.Context, accountManager account.Manager, networks
if serviceManager != nil && reverseProxyDomainManager != nil {
reverseproxymanager.RegisterEndpoints(serviceManager, *reverseProxyDomainManager, reverseProxyAccessLogsManager, permissionsManager, router)
}
// Register OAuth callback handler for proxy authentication
if proxyGRPCServer != nil {
oauthHandler := proxy.NewAuthCallbackHandler(proxyGRPCServer, trustedHTTPProxies)

View File

@@ -220,6 +220,9 @@ func (h *handler) updateAccountRequestSettings(req api.PutApiAccountsAccountIdJS
return nil, fmt.Errorf("invalid AutoUpdateVersion")
}
}
if req.Settings.AutoUpdateAlways != nil {
returnSettings.AutoUpdateAlways = *req.Settings.AutoUpdateAlways
}
return returnSettings, nil
}
@@ -329,6 +332,7 @@ func toAccountResponse(accountID string, settings *types.Settings, meta *types.A
LazyConnectionEnabled: &settings.LazyConnectionEnabled,
DnsDomain: &settings.DNSDomain,
AutoUpdateVersion: &settings.AutoUpdateVersion,
AutoUpdateAlways: &settings.AutoUpdateAlways,
EmbeddedIdpEnabled: &settings.EmbeddedIdpEnabled,
LocalAuthDisabled: &settings.LocalAuthDisabled,
}

View File

@@ -80,7 +80,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}
@@ -133,6 +133,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
RoutingPeerDnsResolutionEnabled: br(false),
LazyConnectionEnabled: br(false),
DnsDomain: sr(""),
AutoUpdateAlways: br(false),
AutoUpdateVersion: sr(""),
EmbeddedIdpEnabled: br(false),
LocalAuthDisabled: br(false),
@@ -158,6 +159,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
RoutingPeerDnsResolutionEnabled: br(false),
LazyConnectionEnabled: br(false),
DnsDomain: sr(""),
AutoUpdateAlways: br(false),
AutoUpdateVersion: sr(""),
EmbeddedIdpEnabled: br(false),
LocalAuthDisabled: br(false),
@@ -183,6 +185,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
RoutingPeerDnsResolutionEnabled: br(false),
LazyConnectionEnabled: br(false),
DnsDomain: sr(""),
AutoUpdateAlways: br(false),
AutoUpdateVersion: sr("latest"),
EmbeddedIdpEnabled: br(false),
LocalAuthDisabled: br(false),
@@ -208,6 +211,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
RoutingPeerDnsResolutionEnabled: br(false),
LazyConnectionEnabled: br(false),
DnsDomain: sr(""),
AutoUpdateAlways: br(false),
AutoUpdateVersion: sr(""),
EmbeddedIdpEnabled: br(false),
LocalAuthDisabled: br(false),
@@ -233,6 +237,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
RoutingPeerDnsResolutionEnabled: br(false),
LazyConnectionEnabled: br(false),
DnsDomain: sr(""),
AutoUpdateAlways: br(false),
AutoUpdateVersion: sr(""),
EmbeddedIdpEnabled: br(false),
LocalAuthDisabled: br(false),
@@ -258,6 +263,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
RoutingPeerDnsResolutionEnabled: br(false),
LazyConnectionEnabled: br(false),
DnsDomain: sr(""),
AutoUpdateAlways: br(false),
AutoUpdateVersion: sr(""),
EmbeddedIdpEnabled: br(false),
LocalAuthDisabled: br(false),

View File

@@ -31,7 +31,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -24,18 +24,6 @@ import (
"github.com/netbirdio/netbird/management/server/mock_server"
)
// wrapHandler wraps a handler function that requires userAuth parameter
func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.UserAuth)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
}
}
const (
existingNSGroupID = "existingNSGroupID"
notFoundNSGroupID = "notFoundNSGroupID"

View File

@@ -171,7 +171,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -41,7 +41,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -95,7 +95,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -42,7 +42,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -73,7 +73,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -21,18 +21,6 @@ import (
"github.com/netbirdio/netbird/shared/management/status"
)
// wrapHandler wraps a handler function that requires userAuth parameter
func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.UserAuth)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
}
}
func initPoliciesTestData(policies ...*types.Policy) *handler {
testPolicies := make(map[string]*types.Policy, len(policies))
for _, policy := range policies {

View File

@@ -26,18 +26,6 @@ import (
var berlin = "Berlin"
var losAngeles = "Los Angeles"
// wrapHandler wraps a handler function that requires userAuth parameter
func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.UserAuth)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
}
}
func initPostureChecksTestData(postureChecks ...*posture.Checks) *postureChecksHandler {
testPostureChecks := make(map[string]*posture.Checks, len(postureChecks))
for _, postureCheck := range postureChecks {

View File

@@ -193,6 +193,9 @@ func setupAuthCallbackTest(t *testing.T) *testSetup {
tokenStore, err := nbgrpc.NewOneTimeTokenStore(ctx, time.Minute, 10*time.Minute, 100)
require.NoError(t, err)
pkceStore, err := nbgrpc.NewPKCEVerifierStore(ctx, 10*time.Minute, 10*time.Minute, 100)
require.NoError(t, err)
usersManager := users.NewManager(testStore)
oidcConfig := nbgrpc.ProxyOIDCConfig{
@@ -206,6 +209,7 @@ func setupAuthCallbackTest(t *testing.T) *testSetup {
proxyService := nbgrpc.NewProxyServiceServer(
&testAccessLogManager{},
tokenStore,
pkceStore,
oidcConfig,
nil,
usersManager,

View File

@@ -33,7 +33,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -30,7 +30,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -32,7 +32,7 @@ func wrapHandler(h func(w http.ResponseWriter, r *http.Request, userAuth *auth.U
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
h(w, r, userAuth)
h(w, r, &userAuth)
}
}

View File

@@ -857,7 +857,7 @@ func TestRejectUserEndpoint(t *testing.T) {
handler := newHandler(am)
router := mux.NewRouter()
router.HandleFunc("/users/{userId}/reject", handler.rejectUser).Methods("DELETE")
router.HandleFunc("/users/{userId}/reject", wrapHandler(handler.rejectUser)).Methods("DELETE")
req, err := http.NewRequest("DELETE", "/users/pending-user/reject", nil)
require.NoError(t, err)
@@ -948,7 +948,7 @@ func TestChangePasswordEndpoint(t *testing.T) {
handler := newHandler(am)
router := mux.NewRouter()
router.HandleFunc("/users/{userId}/password", handler.changePassword).Methods("PUT")
router.HandleFunc("/users/{userId}/password", wrapHandler(handler.changePassword)).Methods("PUT")
reqPath := "/users/" + tc.targetUserID + "/password"
req, err := http.NewRequest("PUT", reqPath, bytes.NewBufferString(tc.requestBody))
@@ -987,7 +987,7 @@ func TestChangePasswordEndpoint_WrongMethod(t *testing.T) {
req = nbcontext.SetUserAuthInRequest(req, userAuth)
rr := httptest.NewRecorder()
handler.changePassword(rr, req)
handler.changePassword(rr, req, &userAuth)
assert.Equal(t, http.StatusMethodNotAllowed, rr.Code)
}

View File

@@ -51,19 +51,28 @@ func GetList() []string {
// This can be used to bypass authz/authn middlewares for certain paths, such as webhooks that implement their own authentication.
func ShouldBypass(requestPath string, h http.Handler, w http.ResponseWriter, r *http.Request) bool {
byPassMutex.RLock()
defer byPassMutex.RUnlock()
var matched bool
for bypassPath := range bypassPaths {
matched, err := path.Match(bypassPath, requestPath)
m, err := path.Match(bypassPath, requestPath)
if err != nil {
log.WithContext(r.Context()).Errorf("Error matching path %s with %s from %s: %v", bypassPath, requestPath, GetList(), err)
list := make([]string, 0, len(bypassPaths))
for k := range bypassPaths {
list = append(list, k)
}
log.WithContext(r.Context()).Errorf("Error matching path %s with %s from %v: %v", bypassPath, requestPath, list, err)
continue
}
if matched {
h.ServeHTTP(w, r)
return true
if m {
matched = true
break
}
}
byPassMutex.RUnlock()
if matched {
h.ServeHTTP(w, r)
return true
}
return false
}

View File

@@ -80,8 +80,8 @@ func BuildApiBlackBoxWithDBState(t testing_tools.TB, sqlFile string, expectedPee
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)
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), settings.IdpConfig{})
peersManager := peers.NewManager(store)
jobManager := job.NewJobManager(nil, store, peersManager)
@@ -93,23 +93,28 @@ func BuildApiBlackBoxWithDBState(t testing_tools.TB, sqlFile string, expectedPee
t.Fatalf("Failed to create manager: %v", err)
}
accessLogsManager := accesslogsmanager.NewManager(store, permissionsManager, nil)
accessLogsManager := accesslogsmanager.NewManager(store, 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, nbgrpc.ProxyOIDCConfig{}, peersManager, userManager, proxyMgr)
domainManager := manager.NewManager(store, proxyMgr, permissionsManager)
proxyServiceServer := nbgrpc.NewProxyServiceServer(accessLogsManager, proxyTokenStore, pkceverifierStore, nbgrpc.ProxyOIDCConfig{}, peersManager, userManager, proxyMgr)
domainManager := manager.NewManager(store, proxyMgr, am)
serviceProxyController, err := proxymanager.NewGRPCController(proxyServiceServer, noopMeter)
if err != nil {
t.Fatalf("Failed to create proxy controller: %v", err)
}
serviceManager := reverseproxymanager.NewManager(store, am, permissionsManager, serviceProxyController, domainManager)
domainManager.SetClusterCapabilities(serviceProxyController)
serviceManager := reverseproxymanager.NewManager(store, am, serviceProxyController, domainManager)
proxyServiceServer.SetServiceManager(serviceManager)
am.SetServiceManager(serviceManager)
@@ -126,8 +131,8 @@ func BuildApiBlackBoxWithDBState(t testing_tools.TB, sqlFile string, expectedPee
resourcesManagerMock := resources.NewManagerMock()
routersManagerMock := routers.NewManagerMock()
groupsManagerMock := groups.NewManagerMock()
customZonesManager := zonesManager.NewManager(store, am, permissionsManager, "")
zoneRecordsManager := recordsManager.NewManager(store, am, permissionsManager)
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)
if err != nil {