mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 11:39:57 +00:00
Merge branch 'main' into feature/buf-cli
# Conflicts: # management/proto/management.pb.go
This commit is contained in:
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
@@ -2,6 +2,10 @@
|
||||
|
||||
## Issue ticket number and link
|
||||
|
||||
## Stack
|
||||
|
||||
<!-- branch-stack -->
|
||||
|
||||
### Checklist
|
||||
- [ ] Is it a bug fix
|
||||
- [ ] Is a typo/documentation fix
|
||||
|
||||
@@ -178,6 +178,7 @@ jobs:
|
||||
grep -A 10 'relay:' docker-compose.yml | egrep 'NB_AUTH_SECRET=.+$'
|
||||
grep -A 7 Relay management.json | grep "rel://$CI_NETBIRD_DOMAIN:33445"
|
||||
grep -A 7 Relay management.json | egrep '"Secret": ".+"'
|
||||
grep DisablePromptLogin management.json | grep 'true'
|
||||
|
||||
- name: Install modules
|
||||
run: go mod tidy
|
||||
|
||||
20
README.md
20
README.md
@@ -57,16 +57,16 @@
|
||||
|
||||
### Key features
|
||||
|
||||
| Connectivity | Management | Security | Automation | Platforms |
|
||||
|------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
|
||||
| <ul><li> - \[x] Kernel WireGuard </ul></li> | <ul><li> - \[x] [Admin Web UI](https://github.com/netbirdio/dashboard) </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] [Public API](https://docs.netbird.io/api) </ul></li> | <ul><li> - \[x] Linux </ul></li> |
|
||||
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
|
||||
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
|
||||
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | <ul><li> - \[x] [Device posture checks](https://docs.netbird.io/how-to/manage-posture-checks) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
|
||||
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | <ul><li> - \[x] Peer-to-peer encryption </ul></li> | | <ul><li> - \[x] iOS </ul></li> |
|
||||
| | | <ul><li> - \[x] [Quantum-resistance with Rosenpass](https://netbird.io/knowledge-hub/the-first-quantum-resistant-mesh-vpn) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
|
||||
| | | <ui><li> - \[x] [Periodic re-authentication](https://docs.netbird.io/how-to/enforce-periodic-user-authentication)</ul></li> | | <ul><li> - \[x] [Serverless](https://docs.netbird.io/how-to/netbird-on-faas) </ul></li> |
|
||||
| | | | | <ul><li> - \[x] Docker </ul></li> |
|
||||
| Connectivity | Management | Security | Automation| Platforms |
|
||||
|----|----|----|----|----|
|
||||
| <ul><li>- \[x] Kernel WireGuard</ul></li> | <ul><li>- \[x] [Admin Web UI](https://github.com/netbirdio/dashboard)</ul></li> | <ul><li>- \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login)</ul></li> | <ul><li>- \[x] [Public API](https://docs.netbird.io/api)</ul></li> | <ul><li>- \[x] Linux</ul></li> |
|
||||
| <ul><li>- \[x] Peer-to-peer connections</ul></li> | <ul><li>- \[x] Auto peer discovery and configuration</ui></li> | <ul><li>- \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access)</ui></li> | <ul><li>- \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys)</ui></li> | <ul><li>- \[x] Mac</ui></li> |
|
||||
| <ul><li>- \[x] Connection relay fallback</ui></li> | <ul><li>- \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers)</ui></li> | <ul><li>- \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity)</ui></li> | <ul><li>- \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart)</ui></li> | <ul><li>- \[x] Windows</ui></li> |
|
||||
| <ul><li>- \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks)</ui></li> | <ul><li>- \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network)</ui></li> | <ul><li>- \[x] [Device posture checks](https://docs.netbird.io/how-to/manage-posture-checks)</ui></li> | <ul><li>- \[x] IdP groups sync with JWT</ui></li> | <ul><li>- \[x] Android</ui></li> |
|
||||
| <ul><li>- \[x] NAT traversal with BPF</ui></li> | <ul><li>- \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network)</ui></li> | <ul><li>- \[x] Peer-to-peer encryption</ui></li> || <ul><li>- \[x] iOS</ui></li> |
|
||||
||| <ul><li>- \[x] [Quantum-resistance with Rosenpass](https://netbird.io/knowledge-hub/the-first-quantum-resistant-mesh-vpn)</ui></li> || <ul><li>- \[x] OpenWRT</ui></li> |
|
||||
||| <ul><li>- \[x] [Periodic re-authentication](https://docs.netbird.io/how-to/enforce-periodic-user-authentication)</ui></li> || <ul><li>- \[x] [Serverless](https://docs.netbird.io/how-to/netbird-on-faas)</ui></li> |
|
||||
||||| <ul><li>- \[x] Docker</ui></li> |
|
||||
|
||||
### Quickstart with NetBird Cloud
|
||||
|
||||
|
||||
@@ -11,9 +11,12 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/debug"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
"github.com/netbirdio/netbird/client/server"
|
||||
nbstatus "github.com/netbirdio/netbird/client/status"
|
||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||
)
|
||||
|
||||
const errCloseConnection = "Failed to close connection: %v"
|
||||
@@ -326,3 +329,34 @@ func formatDuration(d time.Duration) string {
|
||||
s := d / time.Second
|
||||
return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
|
||||
}
|
||||
|
||||
func generateDebugBundle(config *internal.Config, recorder *peer.Status, connectClient *internal.ConnectClient, logFilePath string) {
|
||||
var networkMap *mgmProto.NetworkMap
|
||||
var err error
|
||||
|
||||
if connectClient != nil {
|
||||
networkMap, err = connectClient.GetLatestNetworkMap()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get latest network map: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
bundleGenerator := debug.NewBundleGenerator(
|
||||
debug.GeneratorDependencies{
|
||||
InternalConfig: config,
|
||||
StatusRecorder: recorder,
|
||||
NetworkMap: networkMap,
|
||||
LogFile: logFilePath,
|
||||
},
|
||||
debug.BundleConfig{
|
||||
IncludeSystemInfo: true,
|
||||
},
|
||||
)
|
||||
|
||||
path, err := bundleGenerator.Generate()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to generate debug bundle: %v", err)
|
||||
return
|
||||
}
|
||||
log.Infof("Generated debug bundle from SIGUSR1 at: %s", path)
|
||||
}
|
||||
|
||||
39
client/cmd/debug_unix.go
Normal file
39
client/cmd/debug_unix.go
Normal file
@@ -0,0 +1,39 @@
|
||||
//go:build unix
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
)
|
||||
|
||||
func SetupDebugHandler(
|
||||
ctx context.Context,
|
||||
config *internal.Config,
|
||||
recorder *peer.Status,
|
||||
connectClient *internal.ConnectClient,
|
||||
logFilePath string,
|
||||
) {
|
||||
usr1Ch := make(chan os.Signal, 1)
|
||||
|
||||
signal.Notify(usr1Ch, syscall.SIGUSR1)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-usr1Ch:
|
||||
log.Info("Received SIGUSR1. Triggering debug bundle generation.")
|
||||
go generateDebugBundle(config, recorder, connectClient, logFilePath)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
126
client/cmd/debug_windows.go
Normal file
126
client/cmd/debug_windows.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
)
|
||||
|
||||
const (
|
||||
envListenEvent = "NB_LISTEN_DEBUG_EVENT"
|
||||
debugTriggerEventName = `Global\NetbirdDebugTriggerEvent`
|
||||
|
||||
waitTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// SetupDebugHandler sets up a Windows event to listen for a signal to generate a debug bundle.
|
||||
// Example usage with PowerShell:
|
||||
// $evt = [System.Threading.EventWaitHandle]::OpenExisting("Global\NetbirdDebugTriggerEvent")
|
||||
// $evt.Set()
|
||||
// $evt.Close()
|
||||
func SetupDebugHandler(
|
||||
ctx context.Context,
|
||||
config *internal.Config,
|
||||
recorder *peer.Status,
|
||||
connectClient *internal.ConnectClient,
|
||||
logFilePath string,
|
||||
) {
|
||||
env := os.Getenv(envListenEvent)
|
||||
if env == "" {
|
||||
return
|
||||
}
|
||||
|
||||
listenEvent, err := strconv.ParseBool(env)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse %s: %v", envListenEvent, err)
|
||||
return
|
||||
}
|
||||
if !listenEvent {
|
||||
return
|
||||
}
|
||||
|
||||
eventNamePtr, err := windows.UTF16PtrFromString(debugTriggerEventName)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to convert event name '%s' to UTF16: %v", debugTriggerEventName, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: restrict access by ACL
|
||||
eventHandle, err := windows.CreateEvent(nil, 1, 0, eventNamePtr)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_ALREADY_EXISTS) {
|
||||
log.Warnf("Debug trigger event '%s' already exists. Attempting to open.", debugTriggerEventName)
|
||||
// SYNCHRONIZE is needed for WaitForSingleObject, EVENT_MODIFY_STATE for ResetEvent.
|
||||
eventHandle, err = windows.OpenEvent(windows.SYNCHRONIZE|windows.EVENT_MODIFY_STATE, false, eventNamePtr)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to open existing debug trigger event '%s': %v", debugTriggerEventName, err)
|
||||
return
|
||||
}
|
||||
log.Infof("Successfully opened existing debug trigger event '%s'.", debugTriggerEventName)
|
||||
} else {
|
||||
log.Errorf("Failed to create debug trigger event '%s': %v", debugTriggerEventName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if eventHandle == windows.InvalidHandle {
|
||||
log.Errorf("Obtained an invalid handle for debug trigger event '%s'", debugTriggerEventName)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Debug handler waiting for signal on event: %s", debugTriggerEventName)
|
||||
|
||||
go waitForEvent(ctx, config, recorder, connectClient, logFilePath, eventHandle)
|
||||
}
|
||||
|
||||
func waitForEvent(
|
||||
ctx context.Context,
|
||||
config *internal.Config,
|
||||
recorder *peer.Status,
|
||||
connectClient *internal.ConnectClient,
|
||||
logFilePath string,
|
||||
eventHandle windows.Handle,
|
||||
) {
|
||||
defer func() {
|
||||
if err := windows.CloseHandle(eventHandle); err != nil {
|
||||
log.Errorf("Failed to close debug event handle '%s': %v", debugTriggerEventName, err)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := windows.WaitForSingleObject(eventHandle, uint32(waitTimeout.Milliseconds()))
|
||||
|
||||
switch status {
|
||||
case windows.WAIT_OBJECT_0:
|
||||
log.Info("Received signal on debug event. Triggering debug bundle generation.")
|
||||
|
||||
// reset the event so it can be triggered again later (manual reset == 1)
|
||||
if err := windows.ResetEvent(eventHandle); err != nil {
|
||||
log.Errorf("Failed to reset debug event '%s': %v", debugTriggerEventName, err)
|
||||
}
|
||||
|
||||
go generateDebugBundle(config, recorder, connectClient, logFilePath)
|
||||
case uint32(windows.WAIT_TIMEOUT):
|
||||
|
||||
default:
|
||||
log.Errorf("Unexpected status %d from WaitForSingleObject for debug event '%s': %v", status, debugTriggerEventName, err)
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,6 +115,7 @@ var runCmd = &cobra.Command{
|
||||
|
||||
ctx, cancel := context.WithCancel(cmd.Context())
|
||||
SetupCloseHandler(ctx, cancel)
|
||||
SetupDebugHandler(ctx, nil, nil, nil, logFile)
|
||||
|
||||
s, err := newSVC(newProgram(ctx, cancel), newSVCConfig())
|
||||
if err != nil {
|
||||
|
||||
@@ -92,11 +92,11 @@ func startManagement(t *testing.T, config *types.Config, testFile string) (*grpc
|
||||
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
||||
|
||||
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
if err != nil {
|
||||
|
||||
@@ -219,6 +219,8 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
r.GetFullStatus()
|
||||
|
||||
connectClient := internal.NewConnectClient(ctx, config, r)
|
||||
SetupDebugHandler(ctx, config, r, connectClient, "")
|
||||
|
||||
return connectClient.Run(nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -458,6 +458,6 @@ func newBufferHolder(size int) *bufferHolder {
|
||||
|
||||
func getLogger() logging.LeveledLogger {
|
||||
fac := logging.NewDefaultLoggerFactory()
|
||||
fac.Writer = log.StandardLogger().Writer()
|
||||
//fac.Writer = log.StandardLogger().Writer()
|
||||
return fac.NewLogger("ice")
|
||||
}
|
||||
|
||||
@@ -94,13 +94,17 @@ func (p *PKCEAuthorizationFlow) RequestAuthInfo(ctx context.Context) (AuthFlowIn
|
||||
p.codeVerifier = codeVerifier
|
||||
|
||||
codeChallenge := createCodeChallenge(codeVerifier)
|
||||
authURL := p.oAuthConfig.AuthCodeURL(
|
||||
state,
|
||||
|
||||
params := []oauth2.AuthCodeOption{
|
||||
oauth2.SetAuthURLParam("code_challenge_method", "S256"),
|
||||
oauth2.SetAuthURLParam("code_challenge", codeChallenge),
|
||||
oauth2.SetAuthURLParam("audience", p.providerConfig.Audience),
|
||||
oauth2.SetAuthURLParam("prompt", "login"),
|
||||
)
|
||||
}
|
||||
if !p.providerConfig.DisablePromptLogin {
|
||||
params = append(params, oauth2.SetAuthURLParam("prompt", "login"))
|
||||
}
|
||||
|
||||
authURL := p.oAuthConfig.AuthCodeURL(state, params...)
|
||||
|
||||
return AuthFlowInfo{
|
||||
VerificationURIComplete: authURL,
|
||||
|
||||
49
client/internal/auth/pkce_flow_test.go
Normal file
49
client/internal/auth/pkce_flow_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
)
|
||||
|
||||
func TestPromptLogin(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
prompt bool
|
||||
}{
|
||||
{"PromptLogin", true},
|
||||
{"NoPromptLogin", false},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
config := internal.PKCEAuthProviderConfig{
|
||||
ClientID: "test-client-id",
|
||||
Audience: "test-audience",
|
||||
TokenEndpoint: "https://test-token-endpoint.com/token",
|
||||
Scope: "openid email profile",
|
||||
AuthorizationEndpoint: "https://test-auth-endpoint.com/authorize",
|
||||
RedirectURLs: []string{"http://127.0.0.1:33992/"},
|
||||
UseIDToken: true,
|
||||
DisablePromptLogin: !tc.prompt,
|
||||
}
|
||||
pkce, err := NewPKCEAuthorizationFlow(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create PKCEAuthorizationFlow: %v", err)
|
||||
}
|
||||
authInfo, err := pkce.RequestAuthInfo(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to request auth info: %v", err)
|
||||
}
|
||||
pattern := "prompt=login"
|
||||
if tc.prompt {
|
||||
require.Contains(t, authInfo.VerificationURIComplete, pattern)
|
||||
} else {
|
||||
require.NotContains(t, authInfo.VerificationURIComplete, pattern)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -349,6 +349,25 @@ func (c *ConnectClient) Engine() *Engine {
|
||||
return e
|
||||
}
|
||||
|
||||
// GetLatestNetworkMap returns the latest network map from the engine.
|
||||
func (c *ConnectClient) GetLatestNetworkMap() (*mgmProto.NetworkMap, error) {
|
||||
engine := c.Engine()
|
||||
if engine == nil {
|
||||
return nil, errors.New("engine is not initialized")
|
||||
}
|
||||
|
||||
networkMap, err := engine.GetLatestNetworkMap()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get latest network map: %w", err)
|
||||
}
|
||||
|
||||
if networkMap == nil {
|
||||
return nil, errors.New("network map is not available")
|
||||
}
|
||||
|
||||
return networkMap, nil
|
||||
}
|
||||
|
||||
// Status returns the current client status
|
||||
func (c *ConnectClient) Status() StatusType {
|
||||
if c == nil {
|
||||
|
||||
1022
client/internal/debug/debug.go
Normal file
1022
client/internal/debug/debug.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,8 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package server
|
||||
package debug
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
@@ -14,36 +13,31 @@ import (
|
||||
"github.com/google/nftables"
|
||||
"github.com/google/nftables/expr"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/anonymize"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
// addFirewallRules collects and adds firewall rules to the archive
|
||||
func (s *Server) addFirewallRules(req *proto.DebugBundleRequest, anonymizer *anonymize.Anonymizer, archive *zip.Writer) error {
|
||||
func (g *BundleGenerator) addFirewallRules() error {
|
||||
log.Info("Collecting firewall rules")
|
||||
// Collect and add iptables rules
|
||||
iptablesRules, err := collectIPTablesRules()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to collect iptables rules: %v", err)
|
||||
} else {
|
||||
if req.GetAnonymize() {
|
||||
iptablesRules = anonymizer.AnonymizeString(iptablesRules)
|
||||
if g.anonymize {
|
||||
iptablesRules = g.anonymizer.AnonymizeString(iptablesRules)
|
||||
}
|
||||
if err := addFileToZip(archive, strings.NewReader(iptablesRules), "iptables.txt"); err != nil {
|
||||
if err := g.addFileToZip(strings.NewReader(iptablesRules), "iptables.txt"); err != nil {
|
||||
log.Warnf("Failed to add iptables rules to bundle: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and add nftables rules
|
||||
nftablesRules, err := collectNFTablesRules()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to collect nftables rules: %v", err)
|
||||
} else {
|
||||
if req.GetAnonymize() {
|
||||
nftablesRules = anonymizer.AnonymizeString(nftablesRules)
|
||||
if g.anonymize {
|
||||
nftablesRules = g.anonymizer.AnonymizeString(nftablesRules)
|
||||
}
|
||||
if err := addFileToZip(archive, strings.NewReader(nftablesRules), "nftables.txt"); err != nil {
|
||||
if err := g.addFileToZip(strings.NewReader(nftablesRules), "nftables.txt"); err != nil {
|
||||
log.Warnf("Failed to add nftables rules to bundle: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -65,16 +59,13 @@ func collectIPTablesRules() (string, error) {
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
// Then get verbose statistics for each table
|
||||
builder.WriteString("=== iptables -v -n -L output ===\n")
|
||||
|
||||
// Get list of tables
|
||||
tables := []string{"filter", "nat", "mangle", "raw", "security"}
|
||||
|
||||
for _, table := range tables {
|
||||
builder.WriteString(fmt.Sprintf("*%s\n", table))
|
||||
|
||||
// Get verbose statistics for the entire table
|
||||
stats, err := getTableStatistics(table)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get statistics for table %s: %v", table, err)
|
||||
@@ -182,12 +173,10 @@ func formatTables(conn *nftables.Conn, tables []*nftables.Table) string {
|
||||
continue
|
||||
}
|
||||
|
||||
// Format chains
|
||||
for _, chain := range chains {
|
||||
formatChain(conn, table, chain, &builder)
|
||||
}
|
||||
|
||||
// Format sets
|
||||
if sets, err := conn.GetSets(table); err != nil {
|
||||
log.Warnf("Failed to get sets for table %s: %v", table.Name, err)
|
||||
} else if len(sets) > 0 {
|
||||
7
client/internal/debug/debug_mobile.go
Normal file
7
client/internal/debug/debug_mobile.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build ios || android
|
||||
|
||||
package debug
|
||||
|
||||
func (g *BundleGenerator) addRoutes() error {
|
||||
return nil
|
||||
}
|
||||
8
client/internal/debug/debug_nonlinux.go
Normal file
8
client/internal/debug/debug_nonlinux.go
Normal file
@@ -0,0 +1,8 @@
|
||||
//go:build !linux || android
|
||||
|
||||
package debug
|
||||
|
||||
// collectFirewallRules returns nothing on non-linux systems
|
||||
func (g *BundleGenerator) addFirewallRules() error {
|
||||
return nil
|
||||
}
|
||||
25
client/internal/debug/debug_nonmobile.go
Normal file
25
client/internal/debug/debug_nonmobile.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build !ios && !android
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager/systemops"
|
||||
)
|
||||
|
||||
func (g *BundleGenerator) addRoutes() error {
|
||||
routes, err := systemops.GetRoutesFromTable()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get routes: %w", err)
|
||||
}
|
||||
|
||||
// TODO: get routes including nexthop
|
||||
routesContent := formatRoutes(routes, g.anonymize, g.anonymizer)
|
||||
routesReader := strings.NewReader(routesContent)
|
||||
if err := g.addFileToZip(routesReader, "routes.txt"); err != nil {
|
||||
return fmt.Errorf("add routes file to zip: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package server
|
||||
package debug
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -71,6 +71,12 @@ func (d *localResolver) lookupRecords(r *dns.Msg) []dns.RR {
|
||||
|
||||
value, found := d.records.Load(key)
|
||||
if !found {
|
||||
// alternatively check if we have a cname
|
||||
if question.Qtype != dns.TypeCNAME {
|
||||
r.Question[0].Qtype = dns.TypeCNAME
|
||||
return d.lookupRecords(r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -467,6 +467,11 @@ func (s *DefaultServer) applyHostConfig() {
|
||||
return
|
||||
}
|
||||
|
||||
// prevent reapplying config if we're shutting down
|
||||
if s.ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
config := s.currentConfig
|
||||
|
||||
existingDomains := make(map[string]struct{})
|
||||
|
||||
@@ -3,6 +3,7 @@ package dnsfwd
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
@@ -62,7 +63,6 @@ func (f *DNSForwarder) UpdateDomains(domains []string, resIds map[string]string)
|
||||
|
||||
for _, d := range f.domains {
|
||||
f.mux.HandleRemove(d)
|
||||
f.statusRecorder.RemoveResolvedIPLookupEntry(d)
|
||||
}
|
||||
f.resId.Clear()
|
||||
|
||||
@@ -122,8 +122,8 @@ func (f *DNSForwarder) handleDNSQuery(w dns.ResponseWriter, query *dns.Msg) {
|
||||
return
|
||||
}
|
||||
|
||||
resId, ok := f.resId.Load(strings.TrimSuffix(domain, "."))
|
||||
if ok {
|
||||
resId := f.getResIdForDomain(strings.TrimSuffix(domain, "."))
|
||||
if resId != "" {
|
||||
for _, ip := range ips {
|
||||
var ipWithSuffix string
|
||||
if ip.Is4() {
|
||||
@@ -133,7 +133,7 @@ func (f *DNSForwarder) handleDNSQuery(w dns.ResponseWriter, query *dns.Msg) {
|
||||
ipWithSuffix = ip.String() + "/128"
|
||||
log.Tracef("resolved domain=%s to IPv6=%s", domain, ipWithSuffix)
|
||||
}
|
||||
f.statusRecorder.AddResolvedIPLookupEntry(ipWithSuffix, resId.(string))
|
||||
f.statusRecorder.AddResolvedIPLookupEntry(ipWithSuffix, resId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,36 @@ func (f *DNSForwarder) addIPsToResponse(resp *dns.Msg, domain string, ips []neti
|
||||
}
|
||||
}
|
||||
|
||||
func (f *DNSForwarder) getResIdForDomain(domain string) string {
|
||||
var selectedResId string
|
||||
var bestScore int
|
||||
|
||||
f.resId.Range(func(key, value interface{}) bool {
|
||||
var score int
|
||||
pattern := key.(string)
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(pattern, "*."):
|
||||
baseDomain := strings.TrimPrefix(pattern, "*.")
|
||||
if domain == baseDomain || strings.HasSuffix(domain, "."+baseDomain) {
|
||||
score = len(baseDomain)
|
||||
}
|
||||
case domain == pattern:
|
||||
score = math.MaxInt
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
if score > bestScore {
|
||||
bestScore = score
|
||||
selectedResId = value.(string)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return selectedResId
|
||||
}
|
||||
|
||||
// filterDomains returns a list of normalized domains
|
||||
func filterDomains(domains []string) []string {
|
||||
newDomains := make([]string, 0, len(domains))
|
||||
|
||||
95
client/internal/dnsfwd/forwarder_test.go
Normal file
95
client/internal/dnsfwd/forwarder_test.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package dnsfwd
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetResIdForDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
storedMappings map[string]string // key: domain pattern, value: resId
|
||||
queryDomain string
|
||||
expectedResId string
|
||||
}{
|
||||
{
|
||||
name: "Empty map returns empty string",
|
||||
storedMappings: map[string]string{},
|
||||
queryDomain: "example.com",
|
||||
expectedResId: "",
|
||||
},
|
||||
{
|
||||
name: "Exact match returns stored resId",
|
||||
storedMappings: map[string]string{"example.com": "res1"},
|
||||
queryDomain: "example.com",
|
||||
expectedResId: "res1",
|
||||
},
|
||||
{
|
||||
name: "Wildcard pattern matches base domain",
|
||||
storedMappings: map[string]string{"*.example.com": "res2"},
|
||||
queryDomain: "example.com",
|
||||
expectedResId: "res2",
|
||||
},
|
||||
{
|
||||
name: "Wildcard pattern matches subdomain",
|
||||
storedMappings: map[string]string{"*.example.com": "res3"},
|
||||
queryDomain: "foo.example.com",
|
||||
expectedResId: "res3",
|
||||
},
|
||||
{
|
||||
name: "Wildcard pattern does not match different domain",
|
||||
storedMappings: map[string]string{"*.example.com": "res4"},
|
||||
queryDomain: "foo.notexample.com",
|
||||
expectedResId: "",
|
||||
},
|
||||
{
|
||||
name: "Non-wildcard pattern does not match subdomain",
|
||||
storedMappings: map[string]string{"example.com": "res5"},
|
||||
queryDomain: "foo.example.com",
|
||||
expectedResId: "",
|
||||
},
|
||||
{
|
||||
name: "Exact match over overlapping wildcard",
|
||||
storedMappings: map[string]string{
|
||||
"*.example.com": "resWildcard",
|
||||
"foo.example.com": "resExact",
|
||||
},
|
||||
queryDomain: "foo.example.com",
|
||||
expectedResId: "resExact",
|
||||
},
|
||||
{
|
||||
name: "Overlapping wildcards: Select more specific wildcard",
|
||||
storedMappings: map[string]string{
|
||||
"*.example.com": "resA",
|
||||
"*.sub.example.com": "resB",
|
||||
},
|
||||
queryDomain: "bar.sub.example.com",
|
||||
expectedResId: "resB",
|
||||
},
|
||||
{
|
||||
name: "Wildcard multi-level subdomain match",
|
||||
storedMappings: map[string]string{
|
||||
"*.example.com": "resMulti",
|
||||
},
|
||||
queryDomain: "a.b.example.com",
|
||||
expectedResId: "resMulti",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fwd := &DNSForwarder{
|
||||
resId: sync.Map{},
|
||||
}
|
||||
|
||||
for domainPattern, resId := range tc.storedMappings {
|
||||
fwd.resId.Store(domainPattern, resId)
|
||||
}
|
||||
|
||||
got := fwd.getResIdForDomain(tc.queryDomain)
|
||||
if got != tc.expectedResId {
|
||||
t.Errorf("For query domain %q, expected resId %q, but got %q", tc.queryDomain, tc.expectedResId, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1231,36 +1231,19 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs []netip.Prefix) (*peer
|
||||
PreSharedKey: e.config.PreSharedKey,
|
||||
}
|
||||
|
||||
if e.config.RosenpassEnabled && !e.config.RosenpassPermissive {
|
||||
lk := []byte(e.config.WgPrivateKey.PublicKey().String())
|
||||
rk := []byte(wgConfig.RemoteKey)
|
||||
var keyInput []byte
|
||||
if string(lk) > string(rk) {
|
||||
//nolint:gocritic
|
||||
keyInput = append(lk[:16], rk[:16]...)
|
||||
} else {
|
||||
//nolint:gocritic
|
||||
keyInput = append(rk[:16], lk[:16]...)
|
||||
}
|
||||
|
||||
key, err := wgtypes.NewKey(keyInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wgConfig.PreSharedKey = &key
|
||||
}
|
||||
|
||||
// randomize connection timeout
|
||||
timeout := time.Duration(rand.Intn(PeerConnectionTimeoutMax-PeerConnectionTimeoutMin)+PeerConnectionTimeoutMin) * time.Millisecond
|
||||
config := peer.ConnConfig{
|
||||
Key: pubKey,
|
||||
LocalKey: e.config.WgPrivateKey.PublicKey().String(),
|
||||
Timeout: timeout,
|
||||
WgConfig: wgConfig,
|
||||
LocalWgPort: e.config.WgPort,
|
||||
RosenpassPubKey: e.getRosenpassPubKey(),
|
||||
RosenpassAddr: e.getRosenpassAddr(),
|
||||
Key: pubKey,
|
||||
LocalKey: e.config.WgPrivateKey.PublicKey().String(),
|
||||
Timeout: timeout,
|
||||
WgConfig: wgConfig,
|
||||
LocalWgPort: e.config.WgPort,
|
||||
RosenpassConfig: peer.RosenpassConfig{
|
||||
PubKey: e.getRosenpassPubKey(),
|
||||
Addr: e.getRosenpassAddr(),
|
||||
PermissiveMode: e.config.RosenpassPermissive,
|
||||
},
|
||||
ICEConfig: icemaker.Config{
|
||||
StunTurn: &e.stunTurn,
|
||||
InterfaceBlackList: e.config.IFaceBlackList,
|
||||
|
||||
@@ -1439,8 +1439,6 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
@@ -1449,7 +1447,9 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
|
||||
Return(&types.Settings{}, nil).
|
||||
AnyTimes()
|
||||
|
||||
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
|
||||
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
@@ -60,6 +60,15 @@ type WgConfig struct {
|
||||
PreSharedKey *wgtypes.Key
|
||||
}
|
||||
|
||||
type RosenpassConfig struct {
|
||||
// RosenpassPubKey is this peer's Rosenpass public key
|
||||
PubKey []byte
|
||||
// RosenpassPubKey is this peer's RosenpassAddr server address (IP:port)
|
||||
Addr string
|
||||
|
||||
PermissiveMode bool
|
||||
}
|
||||
|
||||
// ConnConfig is a peer Connection configuration
|
||||
type ConnConfig struct {
|
||||
// Key is a public key of a remote peer
|
||||
@@ -73,10 +82,7 @@ type ConnConfig struct {
|
||||
|
||||
LocalWgPort int
|
||||
|
||||
// RosenpassPubKey is this peer's Rosenpass public key
|
||||
RosenpassPubKey []byte
|
||||
// RosenpassPubKey is this peer's RosenpassAddr server address (IP:port)
|
||||
RosenpassAddr string
|
||||
RosenpassConfig RosenpassConfig
|
||||
|
||||
// ICEConfig ICE protocol configuration
|
||||
ICEConfig icemaker.Config
|
||||
@@ -109,6 +115,8 @@ type Conn struct {
|
||||
connIDICE nbnet.ConnectionID
|
||||
beforeAddPeerHooks []nbnet.AddHookFunc
|
||||
afterRemovePeerHooks []nbnet.RemoveHookFunc
|
||||
// used to store the remote Rosenpass key for Relayed connection in case of connection update from ice
|
||||
rosenpassRemoteKey []byte
|
||||
|
||||
wgProxyICE wgproxy.Proxy
|
||||
wgProxyRelay wgproxy.Proxy
|
||||
@@ -375,7 +383,7 @@ func (conn *Conn) onICEConnectionIsReady(priority ConnPriority, iceConnInfo ICEC
|
||||
wgProxy.Work()
|
||||
}
|
||||
|
||||
if err = conn.configureWGEndpoint(ep); err != nil {
|
||||
if err = conn.configureWGEndpoint(ep, iceConnInfo.RosenpassPubKey); err != nil {
|
||||
conn.handleConfigurationFailure(err, wgProxy)
|
||||
return
|
||||
}
|
||||
@@ -408,7 +416,7 @@ func (conn *Conn) onICEStateDisconnected() {
|
||||
conn.dumpState.SwitchToRelay()
|
||||
conn.wgProxyRelay.Work()
|
||||
|
||||
if err := conn.configureWGEndpoint(conn.wgProxyRelay.EndpointAddr()); err != nil {
|
||||
if err := conn.configureWGEndpoint(conn.wgProxyRelay.EndpointAddr(), conn.rosenpassRemoteKey); err != nil {
|
||||
conn.log.Errorf("failed to switch to relay conn: %v", err)
|
||||
}
|
||||
|
||||
@@ -478,7 +486,7 @@ func (conn *Conn) onRelayConnectionIsReady(rci RelayConnInfo) {
|
||||
}
|
||||
|
||||
wgProxy.Work()
|
||||
if err := conn.configureWGEndpoint(wgProxy.EndpointAddr()); err != nil {
|
||||
if err := conn.configureWGEndpoint(wgProxy.EndpointAddr(), rci.rosenpassPubKey); err != nil {
|
||||
if err := wgProxy.CloseConn(); err != nil {
|
||||
conn.log.Warnf("Failed to close relay connection: %v", err)
|
||||
}
|
||||
@@ -493,6 +501,7 @@ func (conn *Conn) onRelayConnectionIsReady(rci RelayConnInfo) {
|
||||
}()
|
||||
|
||||
wgConfigWorkaround()
|
||||
conn.rosenpassRemoteKey = rci.rosenpassPubKey
|
||||
conn.currentConnPriority = connPriorityRelay
|
||||
conn.statusRelay.Set(StatusConnected)
|
||||
conn.setRelayedProxy(wgProxy)
|
||||
@@ -556,13 +565,14 @@ func (conn *Conn) listenGuardEvent(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) configureWGEndpoint(addr *net.UDPAddr) error {
|
||||
func (conn *Conn) configureWGEndpoint(addr *net.UDPAddr, remoteRPKey []byte) error {
|
||||
presharedKey := conn.presharedKey(remoteRPKey)
|
||||
return conn.config.WgConfig.WgInterface.UpdatePeer(
|
||||
conn.config.WgConfig.RemoteKey,
|
||||
conn.config.WgConfig.AllowedIps,
|
||||
defaultWgKeepAlive,
|
||||
addr,
|
||||
conn.config.WgConfig.PreSharedKey,
|
||||
presharedKey,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -783,6 +793,44 @@ func (conn *Conn) AllowedIP() netip.Addr {
|
||||
return conn.config.WgConfig.AllowedIps[0].Addr()
|
||||
}
|
||||
|
||||
func (conn *Conn) presharedKey(remoteRosenpassKey []byte) *wgtypes.Key {
|
||||
if conn.config.RosenpassConfig.PubKey == nil {
|
||||
return conn.config.WgConfig.PreSharedKey
|
||||
}
|
||||
|
||||
if remoteRosenpassKey == nil && conn.config.RosenpassConfig.PermissiveMode {
|
||||
return conn.config.WgConfig.PreSharedKey
|
||||
}
|
||||
|
||||
determKey, err := conn.rosenpassDetermKey()
|
||||
if err != nil {
|
||||
conn.log.Errorf("failed to generate Rosenpass initial key: %v", err)
|
||||
return conn.config.WgConfig.PreSharedKey
|
||||
}
|
||||
|
||||
return determKey
|
||||
}
|
||||
|
||||
// todo: move this logic into Rosenpass package
|
||||
func (conn *Conn) rosenpassDetermKey() (*wgtypes.Key, error) {
|
||||
lk := []byte(conn.config.LocalKey)
|
||||
rk := []byte(conn.config.Key) // remote key
|
||||
var keyInput []byte
|
||||
if string(lk) > string(rk) {
|
||||
//nolint:gocritic
|
||||
keyInput = append(lk[:16], rk[:16]...)
|
||||
} else {
|
||||
//nolint:gocritic
|
||||
keyInput = append(rk[:16], lk[:16]...)
|
||||
}
|
||||
|
||||
key, err := wgtypes.NewKey(keyInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &key, nil
|
||||
}
|
||||
|
||||
func isController(config ConnConfig) bool {
|
||||
return config.LocalKey > config.Key
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package peer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -161,3 +162,145 @@ func TestConn_Status(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_presharedKey(t *testing.T) {
|
||||
conn1 := Conn{
|
||||
config: ConnConfig{
|
||||
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||
RosenpassConfig: RosenpassConfig{},
|
||||
},
|
||||
}
|
||||
conn2 := Conn{
|
||||
config: ConnConfig{
|
||||
Key: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||
LocalKey: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||
RosenpassConfig: RosenpassConfig{},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
conn1Permissive bool
|
||||
conn1RosenpassEnabled bool
|
||||
conn2Permissive bool
|
||||
conn2RosenpassEnabled bool
|
||||
conn1ExpectedInitialKey bool
|
||||
conn2ExpectedInitialKey bool
|
||||
}{
|
||||
{
|
||||
conn1Permissive: false,
|
||||
conn1RosenpassEnabled: false,
|
||||
conn2Permissive: false,
|
||||
conn2RosenpassEnabled: false,
|
||||
conn1ExpectedInitialKey: false,
|
||||
conn2ExpectedInitialKey: false,
|
||||
},
|
||||
{
|
||||
conn1Permissive: false,
|
||||
conn1RosenpassEnabled: true,
|
||||
conn2Permissive: false,
|
||||
conn2RosenpassEnabled: true,
|
||||
conn1ExpectedInitialKey: true,
|
||||
conn2ExpectedInitialKey: true,
|
||||
},
|
||||
{
|
||||
conn1Permissive: false,
|
||||
conn1RosenpassEnabled: true,
|
||||
conn2Permissive: false,
|
||||
conn2RosenpassEnabled: false,
|
||||
conn1ExpectedInitialKey: true,
|
||||
conn2ExpectedInitialKey: false,
|
||||
},
|
||||
{
|
||||
conn1Permissive: false,
|
||||
conn1RosenpassEnabled: false,
|
||||
conn2Permissive: false,
|
||||
conn2RosenpassEnabled: true,
|
||||
conn1ExpectedInitialKey: false,
|
||||
conn2ExpectedInitialKey: true,
|
||||
},
|
||||
{
|
||||
conn1Permissive: true,
|
||||
conn1RosenpassEnabled: true,
|
||||
conn2Permissive: false,
|
||||
conn2RosenpassEnabled: false,
|
||||
conn1ExpectedInitialKey: false,
|
||||
conn2ExpectedInitialKey: false,
|
||||
},
|
||||
{
|
||||
conn1Permissive: false,
|
||||
conn1RosenpassEnabled: false,
|
||||
conn2Permissive: true,
|
||||
conn2RosenpassEnabled: true,
|
||||
conn1ExpectedInitialKey: false,
|
||||
conn2ExpectedInitialKey: false,
|
||||
},
|
||||
{
|
||||
conn1Permissive: true,
|
||||
conn1RosenpassEnabled: true,
|
||||
conn2Permissive: true,
|
||||
conn2RosenpassEnabled: true,
|
||||
conn1ExpectedInitialKey: true,
|
||||
conn2ExpectedInitialKey: true,
|
||||
},
|
||||
{
|
||||
conn1Permissive: false,
|
||||
conn1RosenpassEnabled: false,
|
||||
conn2Permissive: false,
|
||||
conn2RosenpassEnabled: true,
|
||||
conn1ExpectedInitialKey: false,
|
||||
conn2ExpectedInitialKey: true,
|
||||
},
|
||||
{
|
||||
conn1Permissive: false,
|
||||
conn1RosenpassEnabled: true,
|
||||
conn2Permissive: true,
|
||||
conn2RosenpassEnabled: true,
|
||||
conn1ExpectedInitialKey: true,
|
||||
conn2ExpectedInitialKey: true,
|
||||
},
|
||||
}
|
||||
|
||||
conn1.config.RosenpassConfig.PermissiveMode = true
|
||||
for i, test := range tests {
|
||||
tcase := i + 1
|
||||
t.Run(fmt.Sprintf("Rosenpass test case %d", tcase), func(t *testing.T) {
|
||||
conn1.config.RosenpassConfig = RosenpassConfig{}
|
||||
conn2.config.RosenpassConfig = RosenpassConfig{}
|
||||
|
||||
if test.conn1RosenpassEnabled {
|
||||
conn1.config.RosenpassConfig.PubKey = []byte("dummykey")
|
||||
}
|
||||
conn1.config.RosenpassConfig.PermissiveMode = test.conn1Permissive
|
||||
|
||||
if test.conn2RosenpassEnabled {
|
||||
conn2.config.RosenpassConfig.PubKey = []byte("dummykey")
|
||||
}
|
||||
conn2.config.RosenpassConfig.PermissiveMode = test.conn2Permissive
|
||||
|
||||
conn1PresharedKey := conn1.presharedKey(conn2.config.RosenpassConfig.PubKey)
|
||||
conn2PresharedKey := conn2.presharedKey(conn1.config.RosenpassConfig.PubKey)
|
||||
|
||||
if test.conn1ExpectedInitialKey {
|
||||
if conn1PresharedKey == nil {
|
||||
t.Errorf("Case %d: Expected conn1 to have a non-nil key, but got nil", tcase)
|
||||
}
|
||||
} else {
|
||||
if conn1PresharedKey != nil {
|
||||
t.Errorf("Case %d: Expected conn1 to have a nil key, but got %v", tcase, conn1PresharedKey)
|
||||
}
|
||||
}
|
||||
|
||||
// Assert conn2's key expectation
|
||||
if test.conn2ExpectedInitialKey {
|
||||
if conn2PresharedKey == nil {
|
||||
t.Errorf("Case %d: Expected conn2 to have a non-nil key, but got nil", tcase)
|
||||
}
|
||||
} else {
|
||||
if conn2PresharedKey != nil {
|
||||
t.Errorf("Case %d: Expected conn2 to have a nil key, but got %v", tcase, conn2PresharedKey)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,8 +154,8 @@ func (h *Handshaker) sendOffer() error {
|
||||
IceCredentials: IceCredentials{iceUFrag, icePwd},
|
||||
WgListenPort: h.config.LocalWgPort,
|
||||
Version: version.NetbirdVersion(),
|
||||
RosenpassPubKey: h.config.RosenpassPubKey,
|
||||
RosenpassAddr: h.config.RosenpassAddr,
|
||||
RosenpassPubKey: h.config.RosenpassConfig.PubKey,
|
||||
RosenpassAddr: h.config.RosenpassConfig.Addr,
|
||||
}
|
||||
|
||||
addr, err := h.relay.RelayInstanceAddress()
|
||||
@@ -174,8 +174,8 @@ func (h *Handshaker) sendAnswer() error {
|
||||
IceCredentials: IceCredentials{uFrag, pwd},
|
||||
WgListenPort: h.config.LocalWgPort,
|
||||
Version: version.NetbirdVersion(),
|
||||
RosenpassPubKey: h.config.RosenpassPubKey,
|
||||
RosenpassAddr: h.config.RosenpassAddr,
|
||||
RosenpassPubKey: h.config.RosenpassConfig.PubKey,
|
||||
RosenpassAddr: h.config.RosenpassConfig.Addr,
|
||||
}
|
||||
addr, err := h.relay.RelayInstanceAddress()
|
||||
if err == nil {
|
||||
|
||||
@@ -37,7 +37,8 @@ func NewAgent(iFaceDiscover stdnet.ExternalIFaceDiscover, config Config, candida
|
||||
}
|
||||
|
||||
fac := logging.NewDefaultLoggerFactory()
|
||||
fac.Writer = log.StandardLogger().Writer()
|
||||
|
||||
//fac.Writer = log.StandardLogger().Writer()
|
||||
|
||||
agentConfig := &ice.AgentConfig{
|
||||
MulticastDNSMode: ice.MulticastDNSModeDisabled,
|
||||
|
||||
@@ -39,6 +39,8 @@ type PKCEAuthProviderConfig struct {
|
||||
UseIDToken bool
|
||||
// ClientCertPair is used for mTLS authentication to the IDP
|
||||
ClientCertPair *tls.Certificate
|
||||
// DisablePromptLogin makes the PKCE flow to not prompt the user for login
|
||||
DisablePromptLogin bool
|
||||
}
|
||||
|
||||
// GetPKCEAuthorizationFlowInfo initialize a PKCEAuthorizationFlow instance and return with it
|
||||
@@ -97,6 +99,7 @@ func GetPKCEAuthorizationFlowInfo(ctx context.Context, privateKey string, mgmURL
|
||||
RedirectURLs: protoPKCEAuthorizationFlow.GetProviderConfig().GetRedirectURLs(),
|
||||
UseIDToken: protoPKCEAuthorizationFlow.GetProviderConfig().GetUseIDToken(),
|
||||
ClientCertPair: clientCert,
|
||||
DisablePromptLogin: protoPKCEAuthorizationFlow.GetProviderConfig().GetDisablePromptLogin(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -10,20 +10,27 @@ import (
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/netbirdio/netbird/client/errors"
|
||||
route "github.com/netbirdio/netbird/route"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
type RouteSelector struct {
|
||||
mu sync.RWMutex
|
||||
selectedRoutes map[route.NetID]struct{}
|
||||
selectAll bool
|
||||
|
||||
// Indicates if new routes should be automatically selected
|
||||
includeNewRoutes bool
|
||||
|
||||
// All known routes at the time of deselection
|
||||
knownRoutes []route.NetID
|
||||
}
|
||||
|
||||
func NewRouteSelector() *RouteSelector {
|
||||
return &RouteSelector{
|
||||
selectedRoutes: map[route.NetID]struct{}{},
|
||||
// default selects all routes
|
||||
selectAll: true,
|
||||
selectedRoutes: map[route.NetID]struct{}{},
|
||||
selectAll: true,
|
||||
includeNewRoutes: false,
|
||||
knownRoutes: []route.NetID{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +53,7 @@ func (rs *RouteSelector) SelectRoutes(routes []route.NetID, appendRoute bool, al
|
||||
rs.selectedRoutes[route] = struct{}{}
|
||||
}
|
||||
rs.selectAll = false
|
||||
rs.includeNewRoutes = false
|
||||
|
||||
return errors.FormatErrorOrNil(err)
|
||||
}
|
||||
@@ -57,16 +65,22 @@ func (rs *RouteSelector) SelectAllRoutes() {
|
||||
|
||||
rs.selectAll = true
|
||||
rs.selectedRoutes = map[route.NetID]struct{}{}
|
||||
rs.includeNewRoutes = false
|
||||
}
|
||||
|
||||
// DeselectRoutes removes specific routes from the selection.
|
||||
// If the selector is in "select all" mode, it will transition to "select specific" mode.
|
||||
// If the selector is in "select all" mode, it will transition to "select specific" mode
|
||||
// but will keep new routes selected.
|
||||
func (rs *RouteSelector) DeselectRoutes(routes []route.NetID, allRoutes []route.NetID) error {
|
||||
rs.mu.Lock()
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
if rs.selectAll {
|
||||
rs.selectAll = false
|
||||
rs.includeNewRoutes = true
|
||||
rs.knownRoutes = make([]route.NetID, len(allRoutes))
|
||||
copy(rs.knownRoutes, allRoutes)
|
||||
|
||||
rs.selectedRoutes = map[route.NetID]struct{}{}
|
||||
for _, route := range allRoutes {
|
||||
rs.selectedRoutes[route] = struct{}{}
|
||||
@@ -92,6 +106,7 @@ func (rs *RouteSelector) DeselectAllRoutes() {
|
||||
defer rs.mu.Unlock()
|
||||
|
||||
rs.selectAll = false
|
||||
rs.includeNewRoutes = false
|
||||
rs.selectedRoutes = map[route.NetID]struct{}{}
|
||||
}
|
||||
|
||||
@@ -103,8 +118,20 @@ func (rs *RouteSelector) IsSelected(routeID route.NetID) bool {
|
||||
if rs.selectAll {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the route exists in selectedRoutes
|
||||
_, selected := rs.selectedRoutes[routeID]
|
||||
return selected
|
||||
if selected {
|
||||
return true
|
||||
}
|
||||
|
||||
// If includeNewRoutes is true and this is a new route (not in knownRoutes),
|
||||
// then it should be selected
|
||||
if rs.includeNewRoutes && !slices.Contains(rs.knownRoutes, routeID) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterSelected removes unselected routes from the provided map.
|
||||
@@ -118,7 +145,11 @@ func (rs *RouteSelector) FilterSelected(routes route.HAMap) route.HAMap {
|
||||
|
||||
filtered := route.HAMap{}
|
||||
for id, rt := range routes {
|
||||
if rs.IsSelected(id.NetID()) {
|
||||
netID := id.NetID()
|
||||
_, selected := rs.selectedRoutes[netID]
|
||||
|
||||
// Include if directly selected or if it's a new route and includeNewRoutes is true
|
||||
if selected || (rs.includeNewRoutes && !slices.Contains(rs.knownRoutes, netID)) {
|
||||
filtered[id] = rt
|
||||
}
|
||||
}
|
||||
@@ -131,11 +162,15 @@ func (rs *RouteSelector) MarshalJSON() ([]byte, error) {
|
||||
defer rs.mu.RUnlock()
|
||||
|
||||
return json.Marshal(struct {
|
||||
SelectedRoutes map[route.NetID]struct{} `json:"selected_routes"`
|
||||
SelectAll bool `json:"select_all"`
|
||||
SelectedRoutes map[route.NetID]struct{} `json:"selected_routes"`
|
||||
SelectAll bool `json:"select_all"`
|
||||
IncludeNewRoutes bool `json:"include_new_routes"`
|
||||
KnownRoutes []route.NetID `json:"known_routes"`
|
||||
}{
|
||||
SelectAll: rs.selectAll,
|
||||
SelectedRoutes: rs.selectedRoutes,
|
||||
SelectAll: rs.selectAll,
|
||||
SelectedRoutes: rs.selectedRoutes,
|
||||
IncludeNewRoutes: rs.includeNewRoutes,
|
||||
KnownRoutes: rs.knownRoutes,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -149,12 +184,16 @@ func (rs *RouteSelector) UnmarshalJSON(data []byte) error {
|
||||
if len(data) == 0 || string(data) == "null" {
|
||||
rs.selectedRoutes = map[route.NetID]struct{}{}
|
||||
rs.selectAll = true
|
||||
rs.includeNewRoutes = false
|
||||
rs.knownRoutes = []route.NetID{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var temp struct {
|
||||
SelectedRoutes map[route.NetID]struct{} `json:"selected_routes"`
|
||||
SelectAll bool `json:"select_all"`
|
||||
SelectedRoutes map[route.NetID]struct{} `json:"selected_routes"`
|
||||
SelectAll bool `json:"select_all"`
|
||||
IncludeNewRoutes bool `json:"include_new_routes"`
|
||||
KnownRoutes []route.NetID `json:"known_routes"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &temp); err != nil {
|
||||
@@ -163,10 +202,15 @@ func (rs *RouteSelector) UnmarshalJSON(data []byte) error {
|
||||
|
||||
rs.selectedRoutes = temp.SelectedRoutes
|
||||
rs.selectAll = temp.SelectAll
|
||||
rs.includeNewRoutes = temp.IncludeNewRoutes
|
||||
rs.knownRoutes = temp.KnownRoutes
|
||||
|
||||
if rs.selectedRoutes == nil {
|
||||
rs.selectedRoutes = map[route.NetID]struct{}{}
|
||||
}
|
||||
if rs.knownRoutes == nil {
|
||||
rs.knownRoutes = []route.NetID{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ func TestRouteSelector_NewRoutesBehavior(t *testing.T) {
|
||||
return rs.DeselectRoutes([]route.NetID{"route1"}, initialRoutes)
|
||||
},
|
||||
// After deselecting specific routes, new routes should remain unselected
|
||||
wantNewSelected: []route.NetID{"route2", "route3"},
|
||||
wantNewSelected: []route.NetID{"route2", "route3", "route4", "route5"},
|
||||
},
|
||||
{
|
||||
name: "New routes after selecting with append",
|
||||
@@ -358,3 +358,73 @@ func TestRouteSelector_NewRoutesBehavior(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteSelector_MixedSelectionDeselection(t *testing.T) {
|
||||
allRoutes := []route.NetID{"route1", "route2", "route3"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
routesToSelect []route.NetID
|
||||
selectAppend bool
|
||||
routesToDeselect []route.NetID
|
||||
selectFirst bool
|
||||
wantSelectedFinal []route.NetID
|
||||
}{
|
||||
{
|
||||
name: "1. Select A, then Deselect B",
|
||||
routesToSelect: []route.NetID{"route1"},
|
||||
selectAppend: false,
|
||||
routesToDeselect: []route.NetID{"route2"},
|
||||
selectFirst: true,
|
||||
wantSelectedFinal: []route.NetID{"route1"},
|
||||
},
|
||||
{
|
||||
name: "2. Select A, then Deselect A",
|
||||
routesToSelect: []route.NetID{"route1"},
|
||||
selectAppend: false,
|
||||
routesToDeselect: []route.NetID{"route1"},
|
||||
selectFirst: true,
|
||||
wantSelectedFinal: []route.NetID{},
|
||||
},
|
||||
{
|
||||
name: "3. Deselect A (from all), then Select B",
|
||||
routesToSelect: []route.NetID{"route2"},
|
||||
selectAppend: false,
|
||||
routesToDeselect: []route.NetID{"route1"},
|
||||
selectFirst: false,
|
||||
wantSelectedFinal: []route.NetID{"route2"},
|
||||
},
|
||||
{
|
||||
name: "4. Deselect A (from all), then Select A",
|
||||
routesToSelect: []route.NetID{"route1"},
|
||||
selectAppend: false,
|
||||
routesToDeselect: []route.NetID{"route1"},
|
||||
selectFirst: false,
|
||||
wantSelectedFinal: []route.NetID{"route1"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rs := routeselector.NewRouteSelector()
|
||||
|
||||
var err1, err2 error
|
||||
|
||||
if tt.selectFirst {
|
||||
err1 = rs.SelectRoutes(tt.routesToSelect, tt.selectAppend, allRoutes)
|
||||
require.NoError(t, err1)
|
||||
err2 = rs.DeselectRoutes(tt.routesToDeselect, allRoutes)
|
||||
require.NoError(t, err2)
|
||||
} else {
|
||||
err1 = rs.DeselectRoutes(tt.routesToDeselect, allRoutes)
|
||||
require.NoError(t, err1)
|
||||
err2 = rs.SelectRoutes(tt.routesToSelect, tt.selectAppend, allRoutes)
|
||||
require.NoError(t, err2)
|
||||
}
|
||||
|
||||
for _, r := range allRoutes {
|
||||
assert.Equal(t, slices.Contains(tt.wantSelectedFinal, r), rs.IsSelected(r), "Route %s final state mismatch", r)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
//go:build !linux || android
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
|
||||
"github.com/netbirdio/netbird/client/anonymize"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
// collectFirewallRules returns nothing on non-linux systems
|
||||
func (s *Server) addFirewallRules(req *proto.DebugBundleRequest, anonymizer *anonymize.Anonymizer, archive *zip.Writer) error {
|
||||
return nil
|
||||
}
|
||||
@@ -8,10 +8,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel"
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc"
|
||||
@@ -200,10 +201,10 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
||||
|
||||
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
if err != nil {
|
||||
|
||||
@@ -569,7 +569,7 @@ var file_flow_flow_proto_rawDesc = []byte{
|
||||
0x0a, 0x0f, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x12, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x01, 0x0a, 0x09, 0x46, 0x6c, 0x6f,
|
||||
0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd4, 0x01, 0x0a, 0x09, 0x46, 0x6c, 0x6f,
|
||||
0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49,
|
||||
0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02,
|
||||
@@ -580,67 +580,71 @@ var file_flow_flow_proto_rawDesc = []byte{
|
||||
0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x66, 0x6c,
|
||||
0x6f, 0x77, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x10, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64,
|
||||
0x73, 0x52, 0x0a, 0x66, 0x6c, 0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x29, 0x0a,
|
||||
0x0c, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x12, 0x19, 0x0a,
|
||||
0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x9c, 0x04, 0x0a, 0x0a, 0x46, 0x6c, 0x6f,
|
||||
0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x77, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64,
|
||||
0x12, 0x1e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a,
|
||||
0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x09, 0x64, 0x69, 0x72,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x66,
|
||||
0x6c, 0x6f, 0x77, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64,
|
||||
0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
|
||||
0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
|
||||
0x70, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x07, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x06, 0x64, 0x65, 0x73, 0x74, 0x49, 0x70, 0x12, 0x2d, 0x0a, 0x09, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
|
||||
0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52,
|
||||
0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x69, 0x63, 0x6d,
|
||||
0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66,
|
||||
0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x08,
|
||||
0x69, 0x63, 0x6d, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70,
|
||||
0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x78,
|
||||
0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x61,
|
||||
0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x78, 0x50,
|
||||
0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74,
|
||||
0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65,
|
||||
0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x65,
|
||||
0x73, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0f,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x64, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x49, 0x64, 0x42, 0x11, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x48, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x50, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x65, 0x73, 0x74, 0x50, 0x6f, 0x72,
|
||||
0x74, 0x22, 0x44, 0x0a, 0x08, 0x49, 0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63,
|
||||
0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69,
|
||||
0x63, 0x6d, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x2a, 0x45, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||
0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
|
||||
0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10,
|
||||
0x01, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x02, 0x12,
|
||||
0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x03, 0x2a, 0x3b,
|
||||
0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x11, 0x44,
|
||||
0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
|
||||
0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12,
|
||||
0x0a, 0x0a, 0x06, 0x45, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x32, 0x42, 0x0a, 0x0b, 0x46,
|
||||
0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x45, 0x76,
|
||||
0x65, 0x6e, 0x74, 0x73, 0x12, 0x0f, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x46, 0x6c, 0x6f, 0x77,
|
||||
0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x12, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x46, 0x6c, 0x6f,
|
||||
0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42,
|
||||
0x0c, 0x5a, 0x0a, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x73, 0x52, 0x0a, 0x66, 0x6c, 0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x20, 0x0a,
|
||||
0x0b, 0x69, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x22,
|
||||
0x4b, 0x0a, 0x0c, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x12,
|
||||
0x19, 0x0a, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73,
|
||||
0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x0b, 0x69, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x9c, 0x04, 0x0a,
|
||||
0x0a, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66,
|
||||
0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x66, 0x6c,
|
||||
0x6f, 0x77, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a,
|
||||
0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x0f, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x49, 0x70, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x70,
|
||||
0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x65, 0x73, 0x74, 0x49, 0x70, 0x12, 0x2d,
|
||||
0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x0e, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x48, 0x00, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a,
|
||||
0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x0e, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x48, 0x00, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a,
|
||||
0x72, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x09, 0x72, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
|
||||
0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x09, 0x74, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78,
|
||||
0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x72, 0x78,
|
||||
0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65,
|
||||
0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73,
|
||||
0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x28,
|
||||
0x0a, 0x10, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x64, 0x65, 0x73, 0x74, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x42, 0x11, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x48, 0x0a, 0x08, 0x50,
|
||||
0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x65, 0x73,
|
||||
0x74, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x44, 0x0a, 0x08, 0x49, 0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b,
|
||||
0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x2a, 0x45, 0x0a, 0x04, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e,
|
||||
0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54,
|
||||
0x41, 0x52, 0x54, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e,
|
||||
0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x52, 0x4f, 0x50,
|
||||
0x10, 0x03, 0x2a, 0x3b, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||
0x15, 0x0a, 0x11, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b,
|
||||
0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53,
|
||||
0x53, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x32,
|
||||
0x42, 0x0a, 0x0b, 0x46, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33,
|
||||
0x0a, 0x06, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x0f, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e,
|
||||
0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x12, 0x2e, 0x66, 0x6c, 0x6f, 0x77,
|
||||
0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x22, 0x00, 0x28,
|
||||
0x01, 0x30, 0x01, 0x42, 0x0c, 0x5a, 0x0a, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -58,6 +58,7 @@ NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
|
||||
# PKCE authorization flow
|
||||
NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS=${NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS:-"53000"}
|
||||
NETBIRD_AUTH_PKCE_USE_ID_TOKEN=${NETBIRD_AUTH_PKCE_USE_ID_TOKEN:-false}
|
||||
NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=${NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN:-false}
|
||||
NETBIRD_AUTH_PKCE_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
||||
|
||||
# Dashboard
|
||||
@@ -120,6 +121,7 @@ export NETBIRD_AUTH_DEVICE_AUTH_SCOPE
|
||||
export NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN
|
||||
export NETBIRD_AUTH_PKCE_AUTHORIZATION_ENDPOINT
|
||||
export NETBIRD_AUTH_PKCE_USE_ID_TOKEN
|
||||
export NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN
|
||||
export NETBIRD_AUTH_PKCE_AUDIENCE
|
||||
export NETBIRD_DASH_AUTH_USE_AUDIENCE
|
||||
export NETBIRD_DASH_AUTH_AUDIENCE
|
||||
|
||||
@@ -94,7 +94,8 @@
|
||||
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
||||
"Scope": "$NETBIRD_AUTH_SUPPORTED_SCOPES",
|
||||
"RedirectURLs": [$NETBIRD_AUTH_PKCE_REDIRECT_URLS],
|
||||
"UseIDToken": $NETBIRD_AUTH_PKCE_USE_ID_TOKEN
|
||||
"UseIDToken": $NETBIRD_AUTH_PKCE_USE_ID_TOKEN,
|
||||
"DisablePromptLogin": $NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,3 +27,4 @@ NETBIRD_STORE_CONFIG_ENGINE=$CI_NETBIRD_STORE_CONFIG_ENGINE
|
||||
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=$CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH
|
||||
NETBIRD_TURN_EXTERNAL_IP=1.2.3.4
|
||||
NETBIRD_RELAY_PORT=33445
|
||||
NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=true
|
||||
|
||||
@@ -75,7 +75,6 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
@@ -88,6 +87,18 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
||||
).
|
||||
Return(&types.Settings{}, nil).
|
||||
AnyTimes()
|
||||
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
||||
permissionsManagerMock.
|
||||
EXPECT().
|
||||
ValidateUserPermissions(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).
|
||||
Return(true, nil).
|
||||
AnyTimes()
|
||||
|
||||
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
if err != nil {
|
||||
|
||||
@@ -80,3 +80,16 @@ func (a *UsersAPI) ResendInvitation(ctx context.Context, userID string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Current gets the current user info
|
||||
// See more: https://docs.netbird.io/api/resources/users#retrieve-current-user
|
||||
func (a *UsersAPI) Current(ctx context.Context) (*api.User, error) {
|
||||
resp, err := a.c.newRequest(ctx, "GET", "/api/users/current", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
ret, err := parseResponse[api.User](resp)
|
||||
return &ret, err
|
||||
}
|
||||
|
||||
@@ -196,8 +196,42 @@ func TestUsers_ResendInvitation_Err(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestUsers_Current_200(t *testing.T) {
|
||||
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
|
||||
mux.HandleFunc("/api/users/current", func(w http.ResponseWriter, r *http.Request) {
|
||||
retBytes, _ := json.Marshal(testUser)
|
||||
_, err := w.Write(retBytes)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
ret, err := c.Users.Current(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testUser, *ret)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUsers_Current_Err(t *testing.T) {
|
||||
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
|
||||
mux.HandleFunc("/api/users/current", func(w http.ResponseWriter, r *http.Request) {
|
||||
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
|
||||
w.WriteHeader(400)
|
||||
_, err := w.Write(retBytes)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
ret, err := c.Users.Current(context.Background())
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "No", err.Error())
|
||||
assert.Empty(t, ret)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUsers_Integration(t *testing.T) {
|
||||
withBlackBoxServer(t, func(c *rest.Client) {
|
||||
// rest client PAT is owner's
|
||||
current, err := c.Users.Current(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "a23efe53-63fb-11ec-90d6-0242ac120003", current.Id)
|
||||
assert.Equal(t, "owner", current.Role)
|
||||
|
||||
user, err := c.Users.Create(context.Background(), api.UserCreateRequest{
|
||||
AutoGroups: []string{},
|
||||
Email: ptr("test@example.com"),
|
||||
|
||||
@@ -2140,6 +2140,8 @@ type ProviderConfig struct {
|
||||
AuthorizationEndpoint string `protobuf:"bytes,9,opt,name=AuthorizationEndpoint,proto3" json:"AuthorizationEndpoint,omitempty"`
|
||||
// RedirectURLs handles authorization code from IDP manager
|
||||
RedirectURLs []string `protobuf:"bytes,10,rep,name=RedirectURLs,proto3" json:"RedirectURLs,omitempty"`
|
||||
// DisablePromptLogin makes the PKCE flow to not prompt the user for login
|
||||
DisablePromptLogin bool `protobuf:"varint,11,opt,name=DisablePromptLogin,proto3" json:"DisablePromptLogin,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ProviderConfig) Reset() {
|
||||
@@ -2242,6 +2244,13 @@ func (x *ProviderConfig) GetRedirectURLs() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ProviderConfig) GetDisablePromptLogin() bool {
|
||||
if x != nil {
|
||||
return x.DisablePromptLogin
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Route represents a route.Route object
|
||||
type Route struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -3499,7 +3508,7 @@ var file_management_management_proto_rawDesc = []byte{
|
||||
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
||||
0x66, 0x69, 0x67, 0x22, 0x9a, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72,
|
||||
@@ -3522,6 +3531,9 @@ var file_management_management_proto_rawDesc = []byte{
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c,
|
||||
0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, 0x0a, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73,
|
||||
0x12, 0x2e, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x70,
|
||||
0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x44, 0x69,
|
||||
0x73, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
||||
0x22, 0xed, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65,
|
||||
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74,
|
||||
|
||||
@@ -6,11 +6,14 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
cacheStore "github.com/eko/gocache/lib/v4/store"
|
||||
@@ -30,6 +33,8 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
"github.com/netbirdio/netbird/management/server/settings"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
@@ -92,6 +97,9 @@ type DefaultAccountManager struct {
|
||||
metrics telemetry.AppMetrics
|
||||
|
||||
permissionsManager permissions.Manager
|
||||
|
||||
accountUpdateLocks sync.Map
|
||||
updateAccountPeersBufferInterval atomic.Int64
|
||||
}
|
||||
|
||||
// getJWTGroupsChanges calculates the changes needed to sync a user's JWT groups.
|
||||
@@ -186,6 +194,23 @@ func BuildManager(
|
||||
settingsManager: settingsManager,
|
||||
permissionsManager: permissionsManager,
|
||||
}
|
||||
|
||||
var initialInterval int64
|
||||
intervalStr := os.Getenv("PEER_UPDATE_INTERVAL_MS")
|
||||
interval, err := strconv.Atoi(intervalStr)
|
||||
if err != nil {
|
||||
initialInterval = 1
|
||||
} else {
|
||||
initialInterval = int64(interval) * 10
|
||||
go func() {
|
||||
time.Sleep(30 * time.Second)
|
||||
am.updateAccountPeersBufferInterval.Store(int64(time.Duration(interval) * time.Millisecond))
|
||||
log.WithContext(ctx).Infof("set peer update buffer interval to %dms", interval)
|
||||
}()
|
||||
}
|
||||
am.updateAccountPeersBufferInterval.Store(initialInterval)
|
||||
log.WithContext(ctx).Infof("set peer update buffer interval to %dms", initialInterval)
|
||||
|
||||
accountsCounter, err := store.GetAccountsCounter(ctx)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Error(err)
|
||||
@@ -258,7 +283,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Settings, permissions.Write)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Settings, operations.Update)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate user permissions: %w", err)
|
||||
}
|
||||
@@ -508,7 +533,7 @@ func (am *DefaultAccountManager) DeleteAccount(ctx context.Context, accountID, u
|
||||
return err
|
||||
}
|
||||
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Accounts, permissions.Write)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Accounts, operations.Delete)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to validate user permissions: %w", err)
|
||||
}
|
||||
@@ -1021,13 +1046,12 @@ func (am *DefaultAccountManager) GetAccount(ctx context.Context, accountID strin
|
||||
|
||||
// GetAccountByID returns an account associated with this account ID.
|
||||
func (am *DefaultAccountManager) GetAccountByID(ctx context.Context, accountID string, userID string) (*types.Account, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Accounts, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetAccount(ctx, accountID)
|
||||
@@ -1223,7 +1247,7 @@ func (am *DefaultAccountManager) SyncUserJWTGroups(ctx context.Context, userAuth
|
||||
|
||||
if removedGroupAffectsPeers || newGroupsAffectsPeers {
|
||||
log.WithContext(ctx).Tracef("user %s: JWT group membership changed, updating account peers", userAuth.UserId)
|
||||
am.UpdateAccountPeers(ctx, userAuth.AccountId)
|
||||
am.BufferUpdateAccountPeers(ctx, userAuth.AccountId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1462,7 +1486,7 @@ func (am *DefaultAccountManager) GetDNSDomain() string {
|
||||
|
||||
func (am *DefaultAccountManager) onPeersInvalidated(ctx context.Context, accountID string) {
|
||||
log.WithContext(ctx).Debugf("validated peers has been invalidated for account %s", accountID)
|
||||
am.UpdateAccountPeers(ctx, accountID)
|
||||
am.BufferUpdateAccountPeers(ctx, accountID)
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error) {
|
||||
@@ -1515,19 +1539,13 @@ func (am *DefaultAccountManager) getFreeDNSLabel(ctx context.Context, s store.St
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) GetAccountSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Settings, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() && !user.IsServiceUser {
|
||||
return nil, status.Errorf(status.PermissionDenied, "the user has no permission to access account data")
|
||||
}
|
||||
|
||||
return am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
}
|
||||
|
||||
|
||||
@@ -59,15 +59,15 @@ type Manager interface {
|
||||
GetGroup(ctx context.Context, accountId, groupID, userID string) (*types.Group, error)
|
||||
GetAllGroups(ctx context.Context, accountID, userID string) ([]*types.Group, error)
|
||||
GetGroupByName(ctx context.Context, groupName, accountID string) (*types.Group, error)
|
||||
SaveGroup(ctx context.Context, accountID, userID string, group *types.Group) error
|
||||
SaveGroups(ctx context.Context, accountID, userID string, newGroups []*types.Group) error
|
||||
SaveGroup(ctx context.Context, accountID, userID string, group *types.Group, create bool) error
|
||||
SaveGroups(ctx context.Context, accountID, userID string, newGroups []*types.Group, create bool) error
|
||||
DeleteGroup(ctx context.Context, accountId, userId, groupID string) error
|
||||
DeleteGroups(ctx context.Context, accountId, userId string, groupIDs []string) error
|
||||
GroupAddPeer(ctx context.Context, accountId, groupID, peerID string) error
|
||||
GroupDeletePeer(ctx context.Context, accountId, groupID, peerID string) error
|
||||
GetPeerGroups(ctx context.Context, accountID, peerID string) ([]*types.Group, error)
|
||||
GetPolicy(ctx context.Context, accountID, policyID, userID string) (*types.Policy, error)
|
||||
SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy) (*types.Policy, error)
|
||||
SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy, create bool) (*types.Policy, error)
|
||||
DeletePolicy(ctx context.Context, accountID, policyID, userID string) error
|
||||
ListPolicies(ctx context.Context, accountID, userID string) ([]*types.Policy, error)
|
||||
GetRoute(ctx context.Context, accountID string, routeID route.ID, userID string) (*route.Route, error)
|
||||
@@ -93,7 +93,7 @@ type Manager interface {
|
||||
HasConnectedChannel(peerID string) bool
|
||||
GetExternalCacheManager() ExternalCacheManager
|
||||
GetPostureChecks(ctx context.Context, accountID, postureChecksID, userID string) (*posture.Checks, error)
|
||||
SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error)
|
||||
SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks, create bool) (*posture.Checks, error)
|
||||
DeletePostureChecks(ctx context.Context, accountID, postureChecksID, userID string) error
|
||||
ListPostureChecks(ctx context.Context, accountID, userID string) ([]*posture.Checks, error)
|
||||
GetIdpManager() idp.Manager
|
||||
@@ -114,4 +114,5 @@ type Manager interface {
|
||||
CreateAccountByPrivateDomain(ctx context.Context, initiatorId, domain string) (*types.Account, error)
|
||||
UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error)
|
||||
GetOwnerInfo(ctx context.Context, accountId string) (*types.UserInfo, error)
|
||||
GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*types.UserInfo, error)
|
||||
}
|
||||
|
||||
@@ -1115,7 +1115,7 @@ func TestAccountManager_NetworkUpdates_SaveGroup(t *testing.T) {
|
||||
Name: "GroupA",
|
||||
Peers: []string{},
|
||||
}
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group); err != nil {
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group, true); err != nil {
|
||||
t.Errorf("save group: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -1131,7 +1131,7 @@ func TestAccountManager_NetworkUpdates_SaveGroup(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
|
||||
@@ -1150,7 +1150,7 @@ func TestAccountManager_NetworkUpdates_SaveGroup(t *testing.T) {
|
||||
}()
|
||||
|
||||
group.Peers = []string{peer1.ID, peer2.ID, peer3.ID}
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group); err != nil {
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group, true); err != nil {
|
||||
t.Errorf("save group: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -1192,7 +1192,7 @@ func TestAccountManager_NetworkUpdates_SavePolicy(t *testing.T) {
|
||||
Name: "GroupA",
|
||||
Peers: []string{peer1.ID, peer2.ID},
|
||||
}
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group); err != nil {
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group, true); err != nil {
|
||||
t.Errorf("save group: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -1223,7 +1223,7 @@ func TestAccountManager_NetworkUpdates_SavePolicy(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Errorf("delete default rule: %v", err)
|
||||
return
|
||||
@@ -1240,7 +1240,7 @@ func TestAccountManager_NetworkUpdates_DeletePeer(t *testing.T) {
|
||||
Name: "GroupA",
|
||||
Peers: []string{peer1.ID, peer3.ID},
|
||||
}
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group); err != nil {
|
||||
if err := manager.SaveGroup(context.Background(), account.Id, userID, &group, true); err != nil {
|
||||
t.Errorf("save group: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -1256,7 +1256,7 @@ func TestAccountManager_NetworkUpdates_DeletePeer(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Errorf("save policy: %v", err)
|
||||
return
|
||||
@@ -1295,7 +1295,7 @@ func TestAccountManager_NetworkUpdates_DeleteGroup(t *testing.T) {
|
||||
ID: "groupA",
|
||||
Name: "GroupA",
|
||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||
})
|
||||
}, true)
|
||||
|
||||
require.NoError(t, err, "failed to save group")
|
||||
|
||||
@@ -1315,7 +1315,7 @@ func TestAccountManager_NetworkUpdates_DeleteGroup(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Errorf("save policy: %v", err)
|
||||
return
|
||||
@@ -2794,13 +2794,13 @@ func TestAccount_UserGroupsRemoveFromPeers(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
//type TB interface {
|
||||
// type TB interface {
|
||||
// Cleanup(func())
|
||||
// Helper()
|
||||
// TempDir() string
|
||||
// Errorf(format string, args ...interface{})
|
||||
// Fatalf(format string, args ...interface{})
|
||||
//}
|
||||
// }
|
||||
|
||||
func createManager(t testing.TB) (*DefaultAccountManager, error) {
|
||||
t.Helper()
|
||||
@@ -2816,8 +2816,6 @@ func createManager(t testing.TB) (*DefaultAccountManager, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
|
||||
@@ -2831,7 +2829,9 @@ func createManager(t testing.TB) (*DefaultAccountManager, error) {
|
||||
Return(false, nil).
|
||||
AnyTimes()
|
||||
|
||||
manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
|
||||
manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/proto"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -62,17 +64,12 @@ func (c *DNSConfigCache) SetNameServerGroup(key string, value *proto.NameServerG
|
||||
|
||||
// GetDNSSettings validates a user role and returns the DNS settings for the provided account ID
|
||||
func (am *DefaultAccountManager) GetDNSSettings(ctx context.Context, accountID string, userID string) (*types.DNSSettings, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Dns, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetAccountDNSSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
@@ -84,17 +81,12 @@ func (am *DefaultAccountManager) SaveDNSSettings(ctx context.Context, accountID
|
||||
return status.Errorf(status.InvalidArgument, "the dns settings provided are nil")
|
||||
}
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Dns, operations.Update)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() {
|
||||
return status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var updateAccountPeers bool
|
||||
|
||||
@@ -211,14 +211,13 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
|
||||
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
}
|
||||
|
||||
func createDNSStore(t *testing.T) (store.Store, error) {
|
||||
@@ -505,7 +504,7 @@ func TestDNSAccountPeersUpdate(t *testing.T) {
|
||||
Name: "GroupB",
|
||||
Peers: []string{},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
|
||||
@@ -565,7 +564,7 @@ func TestDNSAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupA",
|
||||
Name: "GroupA",
|
||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -21,17 +23,12 @@ func isEnabled() bool {
|
||||
|
||||
// GetEvents returns a list of activity events of an account
|
||||
func (am *DefaultAccountManager) GetEvents(ctx context.Context, accountID, userID string) ([]*activity.Event, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Events, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !(user.HasAdminPower() || user.IsServiceUser) {
|
||||
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view events")
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
events, err := am.eventStore.Get(ctx, accountID, 0, 10000, true)
|
||||
@@ -56,7 +53,7 @@ func (am *DefaultAccountManager) GetEvents(ctx context.Context, accountID, userI
|
||||
filtered = append(filtered, event)
|
||||
}
|
||||
|
||||
err = am.fillEventsWithUserInfo(ctx, events, accountID, user)
|
||||
err = am.fillEventsWithUserInfo(ctx, events, accountID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,8 +86,8 @@ type eventUserInfo struct {
|
||||
accountId string
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) fillEventsWithUserInfo(ctx context.Context, events []*activity.Event, accountId string, user *types.User) error {
|
||||
eventUserInfo, err := am.getEventsUserInfo(ctx, events, accountId, user)
|
||||
func (am *DefaultAccountManager) fillEventsWithUserInfo(ctx context.Context, events []*activity.Event, accountId string, userId string) error {
|
||||
eventUserInfo, err := am.getEventsUserInfo(ctx, events, accountId, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -105,14 +102,14 @@ func (am *DefaultAccountManager) fillEventsWithUserInfo(ctx context.Context, eve
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) getEventsUserInfo(ctx context.Context, events []*activity.Event, accountId string, user *types.User) (map[string]eventUserInfo, error) {
|
||||
func (am *DefaultAccountManager) getEventsUserInfo(ctx context.Context, events []*activity.Event, accountId string, userId string) (map[string]eventUserInfo, error) {
|
||||
accountUsers, err := am.Store.GetAccountUsers(ctx, store.LockingStrengthShare, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @note check whether using a external initiator user here is an issue
|
||||
userInfos, err := am.BuildUserInfosForAccount(ctx, accountId, user.Id, accountUsers)
|
||||
userInfos, err := am.BuildUserInfosForAccount(ctx, accountId, userId, accountUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -146,10 +143,10 @@ func (am *DefaultAccountManager) getEventsUserInfo(ctx context.Context, events [
|
||||
return eventUserInfos, nil
|
||||
}
|
||||
|
||||
return am.getEventsExternalUserInfo(ctx, externalUserIds, eventUserInfos, user)
|
||||
return am.getEventsExternalUserInfo(ctx, externalUserIds, eventUserInfos, userId)
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) getEventsExternalUserInfo(ctx context.Context, externalUserIds []string, eventUserInfos map[string]eventUserInfo, user *types.User) (map[string]eventUserInfo, error) {
|
||||
func (am *DefaultAccountManager) getEventsExternalUserInfo(ctx context.Context, externalUserIds []string, eventUserInfos map[string]eventUserInfo, userId string) (map[string]eventUserInfo, error) {
|
||||
externalAccountId := ""
|
||||
fetched := make(map[string]struct{})
|
||||
externalUsers := []*types.User{}
|
||||
@@ -182,7 +179,7 @@ func (am *DefaultAccountManager) getEventsExternalUserInfo(ctx context.Context,
|
||||
return eventUserInfos, nil
|
||||
}
|
||||
|
||||
externalUserInfos, err := am.BuildUserInfosForAccount(ctx, externalAccountId, user.Id, externalUsers)
|
||||
externalUserInfos, err := am.BuildUserInfosForAccount(ctx, externalAccountId, userId, externalUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -30,17 +32,13 @@ func (e *GroupLinkError) Error() string {
|
||||
|
||||
// CheckGroupPermissions validates if a user has the necessary permissions to view groups
|
||||
func (am *DefaultAccountManager) CheckGroupPermissions(ctx context.Context, accountID, userID string) error {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Groups, operations.Read)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -68,27 +66,26 @@ func (am *DefaultAccountManager) GetGroupByName(ctx context.Context, groupName,
|
||||
}
|
||||
|
||||
// SaveGroup object of the peers
|
||||
func (am *DefaultAccountManager) SaveGroup(ctx context.Context, accountID, userID string, newGroup *types.Group) error {
|
||||
func (am *DefaultAccountManager) SaveGroup(ctx context.Context, accountID, userID string, newGroup *types.Group, create bool) error {
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
return am.SaveGroups(ctx, accountID, userID, []*types.Group{newGroup})
|
||||
return am.SaveGroups(ctx, accountID, userID, []*types.Group{newGroup}, create)
|
||||
}
|
||||
|
||||
// SaveGroups adds new groups to the account.
|
||||
// Note: This function does not acquire the global lock.
|
||||
// It is the caller's responsibility to ensure proper locking is in place before invoking this method.
|
||||
func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, userID string, groups []*types.Group) error {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, userID string, groups []*types.Group, create bool) error {
|
||||
operation := operations.Create
|
||||
if !create {
|
||||
operation = operations.Update
|
||||
}
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Groups, operation)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var eventsToStore []func()
|
||||
@@ -210,17 +207,12 @@ func (am *DefaultAccountManager) DeleteGroup(ctx context.Context, accountID, use
|
||||
// If an error occurs while deleting a group, the function skips it and continues deleting other groups.
|
||||
// Errors are collected and returned at the end.
|
||||
func (am *DefaultAccountManager) DeleteGroups(ctx context.Context, accountID, userID string, groupIDs []string) error {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Groups, operations.Delete)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var allErrors error
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestDefaultAccountManager_CreateGroup(t *testing.T) {
|
||||
}
|
||||
for _, group := range account.Groups {
|
||||
group.Issued = types.GroupIssuedIntegration
|
||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group, true)
|
||||
if err != nil {
|
||||
t.Errorf("should allow to create %s groups", types.GroupIssuedIntegration)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func TestDefaultAccountManager_CreateGroup(t *testing.T) {
|
||||
|
||||
for _, group := range account.Groups {
|
||||
group.Issued = types.GroupIssuedJWT
|
||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group, true)
|
||||
if err != nil {
|
||||
t.Errorf("should allow to create %s groups", types.GroupIssuedJWT)
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestDefaultAccountManager_CreateGroup(t *testing.T) {
|
||||
for _, group := range account.Groups {
|
||||
group.Issued = types.GroupIssuedAPI
|
||||
group.ID = ""
|
||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group, true)
|
||||
if err == nil {
|
||||
t.Errorf("should not create api group with the same name, %s", group.Name)
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func TestDefaultAccountManager_DeleteGroups(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
err = manager.SaveGroups(context.Background(), account.Id, groupAdminUserID, groups)
|
||||
err = manager.SaveGroups(context.Background(), account.Id, groupAdminUserID, groups, true)
|
||||
assert.NoError(t, err, "Failed to save test groups")
|
||||
|
||||
testCases := []struct {
|
||||
@@ -382,13 +382,13 @@ func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *t
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForRoute)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForRoute2)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForNameServerGroups)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForPolicies)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForSetupKeys)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForUsers)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForIntegration)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForRoute, true)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForRoute2, true)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForNameServerGroups, true)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForPolicies, true)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForSetupKeys, true)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForUsers, true)
|
||||
_ = am.SaveGroup(context.Background(), accountID, groupAdminUserID, groupForIntegration, true)
|
||||
|
||||
acc, err := am.Store.GetAccount(context.Background(), account.Id)
|
||||
if err != nil {
|
||||
@@ -426,7 +426,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
Name: "GroupE",
|
||||
Peers: []string{peer2.ID},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
|
||||
@@ -446,7 +446,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupB",
|
||||
Name: "GroupB",
|
||||
Peers: []string{peer1.ID, peer2.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -524,7 +524,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Saving a group linked to policy should update account peers and send peer update
|
||||
@@ -539,7 +539,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupA",
|
||||
Name: "GroupA",
|
||||
Peers: []string{peer1.ID, peer2.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -608,7 +608,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupC",
|
||||
Name: "GroupC",
|
||||
Peers: []string{peer1.ID, peer3.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -649,7 +649,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupA",
|
||||
Name: "GroupA",
|
||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -676,7 +676,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupD",
|
||||
Name: "GroupD",
|
||||
Peers: []string{peer1.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -723,7 +723,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupE",
|
||||
Name: "GroupE",
|
||||
Peers: []string{peer2.ID, peer3.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
@@ -39,7 +41,7 @@ func NewManager(store store.Store, permissionsManager permissions.Manager, accou
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllGroups(ctx context.Context, accountID, userID string) ([]*types.Group, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Groups, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Groups, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -70,7 +72,7 @@ func (m *managerImpl) GetAllGroupsMap(ctx context.Context, accountID, userID str
|
||||
}
|
||||
|
||||
func (m *managerImpl) AddResourceToGroup(ctx context.Context, accountID, userID, groupID string, resource *types.Resource) error {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Groups, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Groups, operations.Update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -828,6 +828,7 @@ func (s *GRPCServer) GetPKCEAuthorizationFlow(ctx context.Context, req *proto.En
|
||||
Scope: s.config.PKCEAuthorizationFlow.ProviderConfig.Scope,
|
||||
RedirectURLs: s.config.PKCEAuthorizationFlow.ProviderConfig.RedirectURLs,
|
||||
UseIDToken: s.config.PKCEAuthorizationFlow.ProviderConfig.UseIDToken,
|
||||
DisablePromptLogin: s.config.PKCEAuthorizationFlow.ProviderConfig.DisablePromptLogin,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -2397,6 +2397,29 @@ paths:
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/users/current:
|
||||
get:
|
||||
summary: Retrieve current user
|
||||
description: Get information about the current user
|
||||
tags: [ Users ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
responses:
|
||||
'200':
|
||||
description: A User object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/User'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/peers:
|
||||
get:
|
||||
summary: List all Peers
|
||||
|
||||
@@ -230,7 +230,7 @@ type Account struct {
|
||||
|
||||
// AccountExtraSettings defines model for AccountExtraSettings.
|
||||
type AccountExtraSettings struct {
|
||||
// NetworkTrafficLogsEnabled Enables or disables network traffic logs. If enabled, all network traffic logs from peers will be stored.
|
||||
// NetworkTrafficLogsEnabled Enables or disables network traffic logging. If enabled, all network traffic events from peers will be stored.
|
||||
NetworkTrafficLogsEnabled bool `json:"network_traffic_logs_enabled"`
|
||||
|
||||
// NetworkTrafficPacketCounterEnabled Enables or disables network traffic packet counter. If enabled, network packets and their size will be counted and reported. (This can have an slight impact on performance)
|
||||
|
||||
@@ -66,15 +66,13 @@ func NewAPIHandler(
|
||||
|
||||
corsMiddleware := cors.AllowAll()
|
||||
|
||||
acMiddleware := middleware.NewAccessControl(accountManager.GetUserFromUserAuth)
|
||||
|
||||
rootRouter := mux.NewRouter()
|
||||
metricsMiddleware := appMetrics.HTTPMiddleware()
|
||||
|
||||
prefix := apiPrefix
|
||||
router := rootRouter.PathPrefix(prefix).Subrouter()
|
||||
|
||||
router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler, acMiddleware.Handler)
|
||||
router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler)
|
||||
|
||||
if _, err := integrations.RegisterHandlers(ctx, prefix, router, accountManager, integratedValidator, appMetrics.GetMeter(), permissionsManager, peersManager, proxyController, settingsManager); err != nil {
|
||||
return nil, fmt.Errorf("register integrations endpoints: %w", err)
|
||||
@@ -85,6 +83,8 @@ func NewAPIHandler(
|
||||
users.AddEndpoints(accountManager, router)
|
||||
setup_keys.AddEndpoints(accountManager, router)
|
||||
policies.AddEndpoints(accountManager, LocationManager, router)
|
||||
policies.AddPostureCheckEndpoints(accountManager, LocationManager, router)
|
||||
policies.AddLocationsEndpoints(accountManager, LocationManager, permissionsManager, router)
|
||||
groups.AddEndpoints(accountManager, router)
|
||||
routes.AddEndpoints(accountManager, router)
|
||||
dns.AddEndpoints(accountManager, router)
|
||||
|
||||
@@ -143,7 +143,7 @@ func (h *handler) updateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
IntegrationReference: existingGroup.IntegrationReference,
|
||||
}
|
||||
|
||||
if err := h.accountManager.SaveGroup(r.Context(), accountID, userID, &group); err != nil {
|
||||
if err := h.accountManager.SaveGroup(r.Context(), accountID, userID, &group, false); err != nil {
|
||||
log.WithContext(r.Context()).Errorf("failed updating group %s under account %s %v", groupID, accountID, err)
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
@@ -203,7 +203,7 @@ func (h *handler) createGroup(w http.ResponseWriter, r *http.Request) {
|
||||
Issued: types.GroupIssuedAPI,
|
||||
}
|
||||
|
||||
err = h.accountManager.SaveGroup(r.Context(), accountID, userID, &group)
|
||||
err = h.accountManager.SaveGroup(r.Context(), accountID, userID, &group, true)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
|
||||
@@ -35,7 +35,7 @@ var TestPeers = map[string]*nbpeer.Peer{
|
||||
func initGroupTestData(initGroups ...*types.Group) *handler {
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
SaveGroupFunc: func(_ context.Context, accountID, userID string, group *types.Group) error {
|
||||
SaveGroupFunc: func(_ context.Context, accountID, userID string, group *types.Group, create bool) error {
|
||||
if !strings.HasPrefix(group.ID, "id-") {
|
||||
group.ID = "id-was-set"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||
"github.com/netbirdio/netbird/management/server/groups"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
@@ -244,7 +245,7 @@ func (h *Handler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
account, err := h.accountManager.GetAccountByID(r.Context(), accountID, userID)
|
||||
account, err := h.accountManager.GetAccountByID(r.Context(), accountID, activity.SystemInitiator)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -17,6 +18,9 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
@@ -41,6 +45,14 @@ func initGeolocationTestData(t *testing.T) *geolocationsHandler {
|
||||
assert.NoError(t, err)
|
||||
t.Cleanup(func() { _ = geo.Stop() })
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
||||
permissionsManagerMock.
|
||||
EXPECT().
|
||||
ValidateUserPermissions(gomock.Any(), gomock.Any(), gomock.Any(), modules.Policies, operations.Read).
|
||||
Return(true, nil).
|
||||
AnyTimes()
|
||||
|
||||
return &geolocationsHandler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetUserByIDFunc: func(ctx context.Context, id string) (*types.User, error) {
|
||||
@@ -48,6 +60,7 @@ func initGeolocationTestData(t *testing.T) *geolocationsHandler {
|
||||
},
|
||||
},
|
||||
geolocationManager: geo,
|
||||
permissionsManager: permissionsManagerMock,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
)
|
||||
|
||||
@@ -22,19 +25,21 @@ var (
|
||||
type geolocationsHandler struct {
|
||||
accountManager account.Manager
|
||||
geolocationManager geolocation.Geolocation
|
||||
permissionsManager permissions.Manager
|
||||
}
|
||||
|
||||
func addLocationsEndpoint(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) {
|
||||
locationHandler := newGeolocationsHandlerHandler(accountManager, locationManager)
|
||||
func AddLocationsEndpoints(accountManager account.Manager, locationManager geolocation.Geolocation, permissionsManager permissions.Manager, router *mux.Router) {
|
||||
locationHandler := newGeolocationsHandlerHandler(accountManager, locationManager, permissionsManager)
|
||||
router.HandleFunc("/locations/countries", locationHandler.getAllCountries).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/locations/countries/{country}/cities", locationHandler.getCitiesByCountry).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
// newGeolocationsHandlerHandler creates a new Geolocations handler
|
||||
func newGeolocationsHandlerHandler(accountManager account.Manager, geolocationManager geolocation.Geolocation) *geolocationsHandler {
|
||||
func newGeolocationsHandlerHandler(accountManager account.Manager, geolocationManager geolocation.Geolocation, permissionsManager permissions.Manager) *geolocationsHandler {
|
||||
return &geolocationsHandler{
|
||||
accountManager: accountManager,
|
||||
geolocationManager: geolocationManager,
|
||||
permissionsManager: permissionsManager,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,20 +103,22 @@ func (l *geolocationsHandler) getCitiesByCountry(w http.ResponseWriter, r *http.
|
||||
}
|
||||
|
||||
func (l *geolocationsHandler) authenticateUser(r *http.Request) error {
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
||||
ctx := r.Context()
|
||||
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, userID := userAuth.AccountId, userAuth.UserId
|
||||
accountID, userID := userAuth.AccountId, userAuth.UserId
|
||||
|
||||
user, err := l.accountManager.GetUserByID(r.Context(), userID)
|
||||
allowed, err := l.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operations.Read)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() {
|
||||
return status.Errorf(status.PermissionDenied, "user is not allowed to perform this action")
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ func AddEndpoints(accountManager account.Manager, locationManager geolocation.Ge
|
||||
router.HandleFunc("/policies/{policyId}", policiesHandler.updatePolicy).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/policies/{policyId}", policiesHandler.getPolicy).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/policies/{policyId}", policiesHandler.deletePolicy).Methods("DELETE", "OPTIONS")
|
||||
addPostureCheckEndpoint(accountManager, locationManager, router)
|
||||
}
|
||||
|
||||
// newHandler creates a new policies handler
|
||||
@@ -96,7 +95,7 @@ func (h *handler) updatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
h.savePolicy(w, r, accountID, userID, policyID)
|
||||
h.savePolicy(w, r, accountID, userID, policyID, false)
|
||||
}
|
||||
|
||||
// createPolicy handles policy creation request
|
||||
@@ -109,11 +108,11 @@ func (h *handler) createPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
accountID, userID := userAuth.AccountId, userAuth.UserId
|
||||
|
||||
h.savePolicy(w, r, accountID, userID, "")
|
||||
h.savePolicy(w, r, accountID, userID, "", true)
|
||||
}
|
||||
|
||||
// savePolicy handles policy creation and update
|
||||
func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID string, userID string, policyID string) {
|
||||
func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID string, userID string, policyID string, create bool) {
|
||||
var req api.PutApiPoliciesPolicyIdJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
@@ -280,7 +279,7 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
||||
policy.SourcePostureChecks = *req.SourcePostureChecks
|
||||
}
|
||||
|
||||
policy, err := h.accountManager.SavePolicy(r.Context(), accountID, userID, policy)
|
||||
policy, err := h.accountManager.SavePolicy(r.Context(), accountID, userID, policy, create)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
|
||||
@@ -34,7 +34,7 @@ func initPoliciesTestData(policies ...*types.Policy) *handler {
|
||||
}
|
||||
return policy, nil
|
||||
},
|
||||
SavePolicyFunc: func(_ context.Context, _, _ string, policy *types.Policy) (*types.Policy, error) {
|
||||
SavePolicyFunc: func(_ context.Context, _, _ string, policy *types.Policy, create bool) (*types.Policy, error) {
|
||||
if !strings.HasPrefix(policy.ID, "id-") {
|
||||
policy.ID = "id-was-set"
|
||||
policy.Rules[0].ID = "id-was-set"
|
||||
|
||||
@@ -21,14 +21,13 @@ type postureChecksHandler struct {
|
||||
geolocationManager geolocation.Geolocation
|
||||
}
|
||||
|
||||
func addPostureCheckEndpoint(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) {
|
||||
func AddPostureCheckEndpoints(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) {
|
||||
postureCheckHandler := newPostureChecksHandler(accountManager, locationManager)
|
||||
router.HandleFunc("/posture-checks", postureCheckHandler.getAllPostureChecks).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks", postureCheckHandler.createPostureCheck).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.updatePostureCheck).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.getPostureCheck).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.deletePostureCheck).Methods("DELETE", "OPTIONS")
|
||||
addLocationsEndpoint(accountManager, locationManager, router)
|
||||
}
|
||||
|
||||
// newPostureChecksHandler creates a new PostureChecks handler
|
||||
@@ -85,7 +84,7 @@ func (p *postureChecksHandler) updatePostureCheck(w http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
p.savePostureChecks(w, r, accountID, userID, postureChecksID)
|
||||
p.savePostureChecks(w, r, accountID, userID, postureChecksID, false)
|
||||
}
|
||||
|
||||
// createPostureCheck handles posture check creation request
|
||||
@@ -98,7 +97,7 @@ func (p *postureChecksHandler) createPostureCheck(w http.ResponseWriter, r *http
|
||||
|
||||
accountID, userID := userAuth.AccountId, userAuth.UserId
|
||||
|
||||
p.savePostureChecks(w, r, accountID, userID, "")
|
||||
p.savePostureChecks(w, r, accountID, userID, "", true)
|
||||
}
|
||||
|
||||
// getPostureCheck handles a posture check Get request identified by ID
|
||||
@@ -151,7 +150,7 @@ func (p *postureChecksHandler) deletePostureCheck(w http.ResponseWriter, r *http
|
||||
}
|
||||
|
||||
// savePostureChecks handles posture checks create and update
|
||||
func (p *postureChecksHandler) savePostureChecks(w http.ResponseWriter, r *http.Request, accountID, userID, postureChecksID string) {
|
||||
func (p *postureChecksHandler) savePostureChecks(w http.ResponseWriter, r *http.Request, accountID, userID, postureChecksID string, create bool) {
|
||||
var (
|
||||
err error
|
||||
req api.PostureCheckUpdate
|
||||
@@ -176,7 +175,7 @@ func (p *postureChecksHandler) savePostureChecks(w http.ResponseWriter, r *http.
|
||||
return
|
||||
}
|
||||
|
||||
postureChecks, err = p.accountManager.SavePostureChecks(r.Context(), accountID, userID, postureChecks)
|
||||
postureChecks, err = p.accountManager.SavePostureChecks(r.Context(), accountID, userID, postureChecks, create)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
|
||||
@@ -40,7 +40,7 @@ func initPostureChecksTestData(postureChecks ...*posture.Checks) *postureChecksH
|
||||
}
|
||||
return p, nil
|
||||
},
|
||||
SavePostureChecksFunc: func(_ context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error) {
|
||||
SavePostureChecksFunc: func(_ context.Context, accountID, userID string, postureChecks *posture.Checks, create bool) (*posture.Checks, error) {
|
||||
postureChecks.ID = "postureCheck"
|
||||
testPostureChecks[postureChecks.ID] = postureChecks
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ type handler struct {
|
||||
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
|
||||
userHandler := newHandler(accountManager)
|
||||
router.HandleFunc("/users", userHandler.getAllUsers).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/users/current", userHandler.getCurrentUser).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}", userHandler.updateUser).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}", userHandler.deleteUser).Methods("DELETE", "OPTIONS")
|
||||
router.HandleFunc("/users", userHandler.createUser).Methods("POST", "OPTIONS")
|
||||
@@ -259,6 +260,29 @@ func (h *handler) inviteUser(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
func (h *handler) getCurrentUser(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
userAuth, err := nbcontext.GetUserAuthFromContext(ctx)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
accountID, userID := userAuth.AccountId, userAuth.UserId
|
||||
|
||||
user, err := h.accountManager.GetCurrentUserInfo(ctx, accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, toUserResponse(user, userID))
|
||||
}
|
||||
|
||||
func toUserResponse(user *types.UserInfo, currenUserID string) *api.User {
|
||||
autoGroups := user.AutoGroups
|
||||
if autoGroups == nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -123,6 +124,64 @@ func initUsersTestData() *handler {
|
||||
|
||||
return nil
|
||||
},
|
||||
GetCurrentUserInfoFunc: func(ctx context.Context, accountID, userID string) (*types.UserInfo, error) {
|
||||
switch userID {
|
||||
case "not-found":
|
||||
return nil, status.NewUserNotFoundError("not-found")
|
||||
case "not-of-account":
|
||||
return nil, status.NewUserNotPartOfAccountError()
|
||||
case "blocked-user":
|
||||
return nil, status.NewUserBlockedError()
|
||||
case "service-user":
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
case "owner":
|
||||
return &types.UserInfo{
|
||||
ID: "owner",
|
||||
Name: "",
|
||||
Role: "owner",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
Issued: "api",
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "full",
|
||||
},
|
||||
}, nil
|
||||
case "regular-user":
|
||||
return &types.UserInfo{
|
||||
ID: "regular-user",
|
||||
Name: "",
|
||||
Role: "user",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
Issued: "api",
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "limited",
|
||||
},
|
||||
}, nil
|
||||
|
||||
case "admin-user":
|
||||
return &types.UserInfo{
|
||||
ID: "admin-user",
|
||||
Name: "",
|
||||
Role: "admin",
|
||||
Status: "active",
|
||||
IsServiceUser: false,
|
||||
IsBlocked: false,
|
||||
NonDeletable: false,
|
||||
LastLogin: time.Time{},
|
||||
Issued: "api",
|
||||
Permissions: types.UserPermissions{
|
||||
DashboardView: "full",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("user id %s not handled", userID)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -481,3 +540,73 @@ func TestDeleteUser(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentUser(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
expectedStatus int
|
||||
requestAuth nbcontext.UserAuth
|
||||
}{
|
||||
{
|
||||
name: "without auth",
|
||||
expectedStatus: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
name: "user not found",
|
||||
requestAuth: nbcontext.UserAuth{UserId: "not-found"},
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "not of account",
|
||||
requestAuth: nbcontext.UserAuth{UserId: "not-of-account"},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "blocked user",
|
||||
requestAuth: nbcontext.UserAuth{UserId: "blocked-user"},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "service user",
|
||||
requestAuth: nbcontext.UserAuth{UserId: "service-user"},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "owner",
|
||||
requestAuth: nbcontext.UserAuth{UserId: "owner"},
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "regular user",
|
||||
requestAuth: nbcontext.UserAuth{UserId: "regular-user"},
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "admin user",
|
||||
requestAuth: nbcontext.UserAuth{UserId: "admin-user"},
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
userHandler := initUsersTestData()
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/users/current", nil)
|
||||
if tc.requestAuth.UserId != "" {
|
||||
req = nbcontext.SetUserAuthInRequest(req, tc.requestAuth)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
userHandler.getCurrentUser(rr, req)
|
||||
|
||||
res := rr.Result()
|
||||
defer res.Body.Close()
|
||||
|
||||
if status := rr.Code; status != tc.expectedStatus {
|
||||
t.Fatalf("handler returned wrong status code: got %v want %v",
|
||||
status, tc.expectedStatus)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// GetUser function defines a function to fetch user from Account by jwtclaims.AuthorizationClaims
|
||||
type GetUser func(ctx context.Context, userAuth nbcontext.UserAuth) (*types.User, error)
|
||||
|
||||
// AccessControl middleware to restrict to make POST/PUT/DELETE requests by admin only
|
||||
type AccessControl struct {
|
||||
getUser GetUser
|
||||
}
|
||||
|
||||
// NewAccessControl instance constructor
|
||||
func NewAccessControl(getUser GetUser) *AccessControl {
|
||||
return &AccessControl{
|
||||
getUser: getUser,
|
||||
}
|
||||
}
|
||||
|
||||
var tokenPathRegexp = regexp.MustCompile(`^.*/api/users/.*/tokens.*$`)
|
||||
|
||||
// Handler method of the middleware which forbids all modify requests for non admin users
|
||||
func (a *AccessControl) Handler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if bypass.ShouldBypass(r.URL.Path, h, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
userAuth, err := nbcontext.GetUserAuthFromRequest(r)
|
||||
if err != nil {
|
||||
log.WithContext(r.Context()).Errorf("failed to get user auth from request: %s", err)
|
||||
util.WriteError(r.Context(), status.Errorf(status.Unauthorized, "invalid user auth"), w)
|
||||
}
|
||||
|
||||
user, err := a.getUser(r.Context(), userAuth)
|
||||
if err != nil {
|
||||
log.WithContext(r.Context()).Errorf("failed to get user: %s", err)
|
||||
util.WriteError(r.Context(), status.Errorf(status.Unauthorized, "invalid user auth"), w)
|
||||
return
|
||||
}
|
||||
|
||||
if user.IsBlocked() {
|
||||
util.WriteError(r.Context(), status.Errorf(status.PermissionDenied, "the user has no access to the API or is blocked"), w)
|
||||
return
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() {
|
||||
switch r.Method {
|
||||
case http.MethodDelete, http.MethodPost, http.MethodPatch, http.MethodPut:
|
||||
|
||||
if tokenPathRegexp.MatchString(r.URL.Path) {
|
||||
log.WithContext(r.Context()).Debugf("valid Path")
|
||||
h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteError(r.Context(), status.Errorf(status.PermissionDenied, "only users with admin power can perform this operation"), w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
@@ -123,9 +122,9 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
|
||||
validatorMock := server.MocIntegratedValidator{}
|
||||
proxyController := integrations.NewController(store)
|
||||
userManager := users.NewManager(store)
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManagerMock)
|
||||
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManagerMock)
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManager)
|
||||
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManager)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create manager: %v", err)
|
||||
}
|
||||
@@ -143,9 +142,9 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
|
||||
resourcesManagerMock := resources.NewManagerMock()
|
||||
routersManagerMock := routers.NewManagerMock()
|
||||
groupsManagerMock := groups.NewManagerMock()
|
||||
peersManager := peers.NewManager(store, permissionsManagerMock)
|
||||
peersManager := peers.NewManager(store, permissionsManager)
|
||||
|
||||
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManagerMock, peersManager, settingsManager)
|
||||
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManager, peersManager, settingsManager)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create API handler: %v", err)
|
||||
}
|
||||
|
||||
@@ -432,8 +432,6 @@ func startManagementForTest(t *testing.T, testFile string, config *types.Config)
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
@@ -443,8 +441,10 @@ func startManagementForTest(t *testing.T, testFile string, config *types.Config)
|
||||
AnyTimes().
|
||||
Return(&types.Settings{}, nil)
|
||||
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
|
||||
accountManager, err := BuildManager(ctx, store, peersUpdateManager, nil, "", "netbird.selfhosted",
|
||||
eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
|
||||
if err != nil {
|
||||
cleanup()
|
||||
|
||||
@@ -195,7 +195,7 @@ func startServer(
|
||||
Return(&types.Settings{}, nil).
|
||||
AnyTimes()
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(str)
|
||||
accountManager, err := server.BuildManager(
|
||||
context.Background(),
|
||||
str,
|
||||
@@ -210,7 +210,7 @@ func startServer(
|
||||
metrics,
|
||||
port_forwarding.NewControllerMock(),
|
||||
settingsMockManager,
|
||||
permissionsManagerMock,
|
||||
permissionsManager,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating an account manager: %v", err)
|
||||
|
||||
@@ -44,8 +44,8 @@ type MockAccountManager struct {
|
||||
GetGroupFunc func(ctx context.Context, accountID, groupID, userID string) (*types.Group, error)
|
||||
GetAllGroupsFunc func(ctx context.Context, accountID, userID string) ([]*types.Group, error)
|
||||
GetGroupByNameFunc func(ctx context.Context, accountID, groupName string) (*types.Group, error)
|
||||
SaveGroupFunc func(ctx context.Context, accountID, userID string, group *types.Group) error
|
||||
SaveGroupsFunc func(ctx context.Context, accountID, userID string, groups []*types.Group) error
|
||||
SaveGroupFunc func(ctx context.Context, accountID, userID string, group *types.Group, create bool) error
|
||||
SaveGroupsFunc func(ctx context.Context, accountID, userID string, groups []*types.Group, create bool) error
|
||||
DeleteGroupFunc func(ctx context.Context, accountID, userId, groupID string) error
|
||||
DeleteGroupsFunc func(ctx context.Context, accountId, userId string, groupIDs []string) error
|
||||
GroupAddPeerFunc func(ctx context.Context, accountID, groupID, peerID string) error
|
||||
@@ -53,7 +53,7 @@ type MockAccountManager struct {
|
||||
GetPeerGroupsFunc func(ctx context.Context, accountID, peerID string) ([]*types.Group, error)
|
||||
DeleteRuleFunc func(ctx context.Context, accountID, ruleID, userID string) error
|
||||
GetPolicyFunc func(ctx context.Context, accountID, policyID, userID string) (*types.Policy, error)
|
||||
SavePolicyFunc func(ctx context.Context, accountID, userID string, policy *types.Policy) (*types.Policy, error)
|
||||
SavePolicyFunc func(ctx context.Context, accountID, userID string, policy *types.Policy, create bool) (*types.Policy, error)
|
||||
DeletePolicyFunc func(ctx context.Context, accountID, policyID, userID string) error
|
||||
ListPoliciesFunc func(ctx context.Context, accountID, userID string) ([]*types.Policy, error)
|
||||
GetUsersFromAccountFunc func(ctx context.Context, accountID, userID string) (map[string]*types.UserInfo, error)
|
||||
@@ -97,7 +97,7 @@ type MockAccountManager struct {
|
||||
HasConnectedChannelFunc func(peerID string) bool
|
||||
GetExternalCacheManagerFunc func() account.ExternalCacheManager
|
||||
GetPostureChecksFunc func(ctx context.Context, accountID, postureChecksID, userID string) (*posture.Checks, error)
|
||||
SavePostureChecksFunc func(ctx context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error)
|
||||
SavePostureChecksFunc func(ctx context.Context, accountID, userID string, postureChecks *posture.Checks, create bool) (*posture.Checks, error)
|
||||
DeletePostureChecksFunc func(ctx context.Context, accountID, postureChecksID, userID string) error
|
||||
ListPostureChecksFunc func(ctx context.Context, accountID, userID string) ([]*posture.Checks, error)
|
||||
GetIdpManagerFunc func() idp.Manager
|
||||
@@ -115,6 +115,7 @@ type MockAccountManager struct {
|
||||
CreateAccountByPrivateDomainFunc func(ctx context.Context, initiatorId, domain string) (*types.Account, error)
|
||||
UpdateToPrimaryAccountFunc func(ctx context.Context, accountId string) (*types.Account, error)
|
||||
GetOwnerInfoFunc func(ctx context.Context, accountID string) (*types.UserInfo, error)
|
||||
GetCurrentUserInfoFunc func(ctx context.Context, accountID, userID string) (*types.UserInfo, error)
|
||||
}
|
||||
|
||||
func (am *MockAccountManager) UpdateAccountPeers(ctx context.Context, accountID string) {
|
||||
@@ -322,17 +323,17 @@ func (am *MockAccountManager) GetGroupByName(ctx context.Context, accountID, gro
|
||||
}
|
||||
|
||||
// SaveGroup mock implementation of SaveGroup from server.AccountManager interface
|
||||
func (am *MockAccountManager) SaveGroup(ctx context.Context, accountID, userID string, group *types.Group) error {
|
||||
func (am *MockAccountManager) SaveGroup(ctx context.Context, accountID, userID string, group *types.Group, create bool) error {
|
||||
if am.SaveGroupFunc != nil {
|
||||
return am.SaveGroupFunc(ctx, accountID, userID, group)
|
||||
return am.SaveGroupFunc(ctx, accountID, userID, group, create)
|
||||
}
|
||||
return status.Errorf(codes.Unimplemented, "method SaveGroup is not implemented")
|
||||
}
|
||||
|
||||
// SaveGroups mock implementation of SaveGroups from server.AccountManager interface
|
||||
func (am *MockAccountManager) SaveGroups(ctx context.Context, accountID, userID string, groups []*types.Group) error {
|
||||
func (am *MockAccountManager) SaveGroups(ctx context.Context, accountID, userID string, groups []*types.Group, create bool) error {
|
||||
if am.SaveGroupsFunc != nil {
|
||||
return am.SaveGroupsFunc(ctx, accountID, userID, groups)
|
||||
return am.SaveGroupsFunc(ctx, accountID, userID, groups, create)
|
||||
}
|
||||
return status.Errorf(codes.Unimplemented, "method SaveGroups is not implemented")
|
||||
}
|
||||
@@ -386,9 +387,9 @@ func (am *MockAccountManager) GetPolicy(ctx context.Context, accountID, policyID
|
||||
}
|
||||
|
||||
// SavePolicy mock implementation of SavePolicy from server.AccountManager interface
|
||||
func (am *MockAccountManager) SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy) (*types.Policy, error) {
|
||||
func (am *MockAccountManager) SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy, create bool) (*types.Policy, error) {
|
||||
if am.SavePolicyFunc != nil {
|
||||
return am.SavePolicyFunc(ctx, accountID, userID, policy)
|
||||
return am.SavePolicyFunc(ctx, accountID, userID, policy, create)
|
||||
}
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SavePolicy is not implemented")
|
||||
}
|
||||
@@ -722,9 +723,9 @@ func (am *MockAccountManager) GetPostureChecks(ctx context.Context, accountID, p
|
||||
}
|
||||
|
||||
// SavePostureChecks mocks SavePostureChecks of the AccountManager interface
|
||||
func (am *MockAccountManager) SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error) {
|
||||
func (am *MockAccountManager) SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks, create bool) (*posture.Checks, error) {
|
||||
if am.SavePostureChecksFunc != nil {
|
||||
return am.SavePostureChecksFunc(ctx, accountID, userID, postureChecks)
|
||||
return am.SavePostureChecksFunc(ctx, accountID, userID, postureChecks, create)
|
||||
}
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SavePostureChecks is not implemented")
|
||||
}
|
||||
@@ -871,3 +872,10 @@ func (am *MockAccountManager) GetOwnerInfo(ctx context.Context, accountId string
|
||||
}
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetOwnerInfo is not implemented")
|
||||
}
|
||||
|
||||
func (am *MockAccountManager) GetCurrentUserInfo(ctx context.Context, accountID, userID string) (*types.UserInfo, error) {
|
||||
if am.GetCurrentUserInfoFunc != nil {
|
||||
return am.GetCurrentUserInfoFunc(ctx, accountID, userID)
|
||||
}
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetCurrentUserInfo is not implemented")
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -20,17 +22,12 @@ const domainPattern = `^(?i)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$`
|
||||
|
||||
// GetNameServerGroup gets a nameserver group object from account and nameserver group IDs
|
||||
func (am *DefaultAccountManager) GetNameServerGroup(ctx context.Context, accountID, userID, nsGroupID string) (*nbdns.NameServerGroup, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Nameservers, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetNameServerGroupByID(ctx, store.LockingStrengthShare, accountID, nsGroupID)
|
||||
@@ -41,13 +38,12 @@ func (am *DefaultAccountManager) CreateNameServerGroup(ctx context.Context, acco
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Nameservers, operations.Create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
newNSGroup := &nbdns.NameServerGroup{
|
||||
@@ -103,13 +99,12 @@ func (am *DefaultAccountManager) SaveNameServerGroup(ctx context.Context, accoun
|
||||
return status.Errorf(status.InvalidArgument, "nameserver group provided is nil")
|
||||
}
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Nameservers, operations.Update)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var updateAccountPeers bool
|
||||
@@ -154,13 +149,12 @@ func (am *DefaultAccountManager) DeleteNameServerGroup(ctx context.Context, acco
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Nameservers, operations.Delete)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var nsGroup *nbdns.NameServerGroup
|
||||
@@ -198,17 +192,12 @@ func (am *DefaultAccountManager) DeleteNameServerGroup(ctx context.Context, acco
|
||||
|
||||
// ListNameServerGroups returns a list of nameserver groups from account
|
||||
func (am *DefaultAccountManager) ListNameServerGroups(ctx context.Context, accountID string, userID string) ([]*nbdns.NameServerGroup, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Nameservers, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetAccountNameServerGroups(ctx, store.LockingStrengthShare, accountID)
|
||||
|
||||
@@ -775,12 +775,11 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
|
||||
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
}
|
||||
|
||||
func createNSStore(t *testing.T) (store.Store, error) {
|
||||
@@ -966,7 +965,7 @@ func TestNameServerAccountPeersUpdate(t *testing.T) {
|
||||
Name: "GroupB",
|
||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||
"github.com/netbirdio/netbird/management/server/networks/types"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
)
|
||||
@@ -46,7 +48,7 @@ func NewManager(store store.Store, permissionsManager permissions.Manager, resou
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllNetworks(ctx context.Context, accountID, userID string) ([]*types.Network, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -58,7 +60,7 @@ func (m *managerImpl) GetAllNetworks(ctx context.Context, accountID, userID stri
|
||||
}
|
||||
|
||||
func (m *managerImpl) CreateNetwork(ctx context.Context, userID string, network *types.Network) (*types.Network, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, modules.Networks, operations.Create)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -82,7 +84,7 @@ func (m *managerImpl) CreateNetwork(ctx context.Context, userID string, network
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetNetwork(ctx context.Context, accountID, userID, networkID string) (*types.Network, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -94,7 +96,7 @@ func (m *managerImpl) GetNetwork(ctx context.Context, accountID, userID, network
|
||||
}
|
||||
|
||||
func (m *managerImpl) UpdateNetwork(ctx context.Context, userID string, network *types.Network) (*types.Network, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, modules.Networks, operations.Update)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -116,7 +118,7 @@ func (m *managerImpl) UpdateNetwork(ctx context.Context, userID string, network
|
||||
}
|
||||
|
||||
func (m *managerImpl) DeleteNetwork(ctx context.Context, accountID, userID, networkID string) error {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Delete)
|
||||
if err != nil {
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
func Test_GetAllNetworksReturnsNetworks(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../testdata/networks.sql", t.TempDir())
|
||||
if err != nil {
|
||||
@@ -26,7 +26,7 @@ func Test_GetAllNetworksReturnsNetworks(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -41,7 +41,7 @@ func Test_GetAllNetworksReturnsNetworks(t *testing.T) {
|
||||
func Test_GetAllNetworksReturnsPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../testdata/networks.sql", t.TempDir())
|
||||
if err != nil {
|
||||
@@ -49,7 +49,7 @@ func Test_GetAllNetworksReturnsPermissionDenied(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -63,7 +63,7 @@ func Test_GetAllNetworksReturnsPermissionDenied(t *testing.T) {
|
||||
func Test_GetNetworkReturnsNetwork(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../testdata/networks.sql", t.TempDir())
|
||||
@@ -72,7 +72,7 @@ func Test_GetNetworkReturnsNetwork(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -86,7 +86,7 @@ func Test_GetNetworkReturnsNetwork(t *testing.T) {
|
||||
func Test_GetNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../testdata/networks.sql", t.TempDir())
|
||||
@@ -95,7 +95,7 @@ func Test_GetNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -108,7 +108,7 @@ func Test_GetNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
|
||||
func Test_CreateNetworkSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
network := &types.Network{
|
||||
AccountID: "testAccountId",
|
||||
Name: "new-network",
|
||||
@@ -120,7 +120,7 @@ func Test_CreateNetworkSuccessfully(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -133,7 +133,7 @@ func Test_CreateNetworkSuccessfully(t *testing.T) {
|
||||
|
||||
func Test_CreateNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
network := &types.Network{
|
||||
AccountID: "testAccountId",
|
||||
Name: "new-network",
|
||||
@@ -145,7 +145,7 @@ func Test_CreateNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -159,7 +159,7 @@ func Test_CreateNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
func Test_DeleteNetworkSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../testdata/networks.sql", t.TempDir())
|
||||
@@ -168,7 +168,7 @@ func Test_DeleteNetworkSuccessfully(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -181,7 +181,7 @@ func Test_DeleteNetworkSuccessfully(t *testing.T) {
|
||||
func Test_DeleteNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../testdata/networks.sql", t.TempDir())
|
||||
@@ -190,7 +190,7 @@ func Test_DeleteNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -202,7 +202,7 @@ func Test_DeleteNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
|
||||
func Test_UpdateNetworkSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
network := &types.Network{
|
||||
AccountID: "testAccountId",
|
||||
ID: "testNetworkId",
|
||||
@@ -215,7 +215,7 @@ func Test_UpdateNetworkSuccessfully(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
@@ -228,7 +228,7 @@ func Test_UpdateNetworkSuccessfully(t *testing.T) {
|
||||
|
||||
func Test_UpdateNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
network := &types.Network{
|
||||
AccountID: "testAccountId",
|
||||
ID: "testNetworkId",
|
||||
@@ -242,7 +242,7 @@ func Test_UpdateNetworkFailsWithPermissionDenied(t *testing.T) {
|
||||
t.Cleanup(cleanUp)
|
||||
|
||||
am := mock_server.MockAccountManager{}
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
groupsManager := groups.NewManagerMock()
|
||||
routerManager := routers.NewManagerMock()
|
||||
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/groups"
|
||||
"github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
nbtypes "github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -47,7 +49,7 @@ func NewManager(store store.Store, permissionsManager permissions.Manager, group
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllResourcesInNetwork(ctx context.Context, accountID, userID, networkID string) ([]*types.NetworkResource, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -59,7 +61,7 @@ func (m *managerImpl) GetAllResourcesInNetwork(ctx context.Context, accountID, u
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllResourcesInAccount(ctx context.Context, accountID, userID string) ([]*types.NetworkResource, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -71,7 +73,7 @@ func (m *managerImpl) GetAllResourcesInAccount(ctx context.Context, accountID, u
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllResourceIDsInAccount(ctx context.Context, accountID, userID string) (map[string][]string, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -93,7 +95,7 @@ func (m *managerImpl) GetAllResourceIDsInAccount(ctx context.Context, accountID,
|
||||
}
|
||||
|
||||
func (m *managerImpl) CreateResource(ctx context.Context, userID string, resource *types.NetworkResource) (*types.NetworkResource, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, resource.AccountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, resource.AccountID, userID, modules.Networks, operations.Create)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -164,7 +166,7 @@ func (m *managerImpl) CreateResource(ctx context.Context, userID string, resourc
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetResource(ctx context.Context, accountID, userID, networkID, resourceID string) (*types.NetworkResource, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -185,7 +187,7 @@ func (m *managerImpl) GetResource(ctx context.Context, accountID, userID, networ
|
||||
}
|
||||
|
||||
func (m *managerImpl) UpdateResource(ctx context.Context, userID string, resource *types.NetworkResource) (*types.NetworkResource, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, resource.AccountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, resource.AccountID, userID, modules.Networks, operations.Update)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -305,7 +307,7 @@ func (m *managerImpl) updateResourceGroups(ctx context.Context, transaction stor
|
||||
}
|
||||
|
||||
func (m *managerImpl) DeleteResource(ctx context.Context, accountID, userID, networkID, resourceID string) error {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Delete)
|
||||
if err != nil {
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
func Test_GetAllResourcesInNetworkReturnsResources(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../../testdata/networks.sql", t.TempDir())
|
||||
@@ -25,7 +25,7 @@ func Test_GetAllResourcesInNetworkReturnsResources(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -38,7 +38,7 @@ func Test_GetAllResourcesInNetworkReturnsResources(t *testing.T) {
|
||||
func Test_GetAllResourcesInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../../testdata/networks.sql", t.TempDir())
|
||||
@@ -46,7 +46,7 @@ func Test_GetAllResourcesInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -59,14 +59,14 @@ func Test_GetAllResourcesInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
func Test_GetAllResourcesInAccountReturnsResources(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
|
||||
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../../testdata/networks.sql", t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -79,14 +79,14 @@ func Test_GetAllResourcesInAccountReturnsResources(t *testing.T) {
|
||||
func Test_GetAllResourcesInAccountReturnsPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
|
||||
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../../testdata/networks.sql", t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -100,7 +100,7 @@ func Test_GetAllResourcesInAccountReturnsPermissionDenied(t *testing.T) {
|
||||
func Test_GetResourceInNetworkReturnsResources(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testResourceId"
|
||||
|
||||
@@ -109,7 +109,7 @@ func Test_GetResourceInNetworkReturnsResources(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -122,7 +122,7 @@ func Test_GetResourceInNetworkReturnsResources(t *testing.T) {
|
||||
func Test_GetResourceInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testResourceId"
|
||||
|
||||
@@ -131,7 +131,7 @@ func Test_GetResourceInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -144,7 +144,7 @@ func Test_GetResourceInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
|
||||
func Test_CreateResourceSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
resource := &types.NetworkResource{
|
||||
AccountID: "testAccountId",
|
||||
NetworkID: "testNetworkId",
|
||||
@@ -158,7 +158,7 @@ func Test_CreateResourceSuccessfully(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -170,7 +170,7 @@ func Test_CreateResourceSuccessfully(t *testing.T) {
|
||||
|
||||
func Test_CreateResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
resource := &types.NetworkResource{
|
||||
AccountID: "testAccountId",
|
||||
NetworkID: "testNetworkId",
|
||||
@@ -184,7 +184,7 @@ func Test_CreateResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -197,7 +197,7 @@ func Test_CreateResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
|
||||
func Test_CreateResourceFailsWithInvalidAddress(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
resource := &types.NetworkResource{
|
||||
AccountID: "testAccountId",
|
||||
NetworkID: "testNetworkId",
|
||||
@@ -211,7 +211,7 @@ func Test_CreateResourceFailsWithInvalidAddress(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -223,7 +223,7 @@ func Test_CreateResourceFailsWithInvalidAddress(t *testing.T) {
|
||||
|
||||
func Test_CreateResourceFailsWithUsedName(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
resource := &types.NetworkResource{
|
||||
AccountID: "testAccountId",
|
||||
NetworkID: "testNetworkId",
|
||||
@@ -237,7 +237,7 @@ func Test_CreateResourceFailsWithUsedName(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -250,7 +250,7 @@ func Test_CreateResourceFailsWithUsedName(t *testing.T) {
|
||||
func Test_UpdateResourceSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testResourceId"
|
||||
resource := &types.NetworkResource{
|
||||
@@ -267,7 +267,7 @@ func Test_UpdateResourceSuccessfully(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -283,7 +283,7 @@ func Test_UpdateResourceSuccessfully(t *testing.T) {
|
||||
func Test_UpdateResourceFailsWithResourceNotFound(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "otherResourceId"
|
||||
resource := &types.NetworkResource{
|
||||
@@ -299,7 +299,7 @@ func Test_UpdateResourceFailsWithResourceNotFound(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -312,7 +312,7 @@ func Test_UpdateResourceFailsWithResourceNotFound(t *testing.T) {
|
||||
func Test_UpdateResourceFailsWithNameInUse(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testResourceId"
|
||||
resource := &types.NetworkResource{
|
||||
@@ -329,7 +329,7 @@ func Test_UpdateResourceFailsWithNameInUse(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -342,7 +342,7 @@ func Test_UpdateResourceFailsWithNameInUse(t *testing.T) {
|
||||
func Test_UpdateResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testResourceId"
|
||||
resource := &types.NetworkResource{
|
||||
@@ -358,7 +358,7 @@ func Test_UpdateResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -371,7 +371,7 @@ func Test_UpdateResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
func Test_DeleteResourceSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testResourceId"
|
||||
|
||||
@@ -380,7 +380,7 @@ func Test_DeleteResourceSuccessfully(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
@@ -392,7 +392,7 @@ func Test_DeleteResourceSuccessfully(t *testing.T) {
|
||||
func Test_DeleteResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testResourceId"
|
||||
|
||||
@@ -401,7 +401,7 @@ func Test_DeleteResourceFailsWithPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
am := mock_server.MockAccountManager{}
|
||||
groupsManager := groups.NewManagerMock()
|
||||
manager := NewManager(store, permissionsManager, groupsManager, &am)
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
)
|
||||
@@ -44,7 +46,7 @@ func NewManager(store store.Store, permissionsManager permissions.Manager, accou
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllRoutersInNetwork(ctx context.Context, accountID, userID, networkID string) ([]*types.NetworkRouter, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -56,7 +58,7 @@ func (m *managerImpl) GetAllRoutersInNetwork(ctx context.Context, accountID, use
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllRoutersInAccount(ctx context.Context, accountID, userID string) (map[string][]*types.NetworkRouter, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -78,7 +80,7 @@ func (m *managerImpl) GetAllRoutersInAccount(ctx context.Context, accountID, use
|
||||
}
|
||||
|
||||
func (m *managerImpl) CreateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, router.AccountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, router.AccountID, userID, modules.Networks, operations.Create)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -126,7 +128,7 @@ func (m *managerImpl) CreateRouter(ctx context.Context, userID string, router *t
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetRouter(ctx context.Context, accountID, userID, networkID, routerID string) (*types.NetworkRouter, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -147,7 +149,7 @@ func (m *managerImpl) GetRouter(ctx context.Context, accountID, userID, networkI
|
||||
}
|
||||
|
||||
func (m *managerImpl) UpdateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error) {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, router.AccountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, router.AccountID, userID, modules.Networks, operations.Update)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
@@ -193,7 +195,7 @@ func (m *managerImpl) UpdateRouter(ctx context.Context, userID string, router *t
|
||||
}
|
||||
|
||||
func (m *managerImpl) DeleteRouter(ctx context.Context, accountID, userID, networkID, routerID string) error {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Write)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Delete)
|
||||
if err != nil {
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
func Test_GetAllRoutersInNetworkReturnsRouters(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../../testdata/networks.sql", t.TempDir())
|
||||
@@ -24,7 +24,7 @@ func Test_GetAllRoutersInNetworkReturnsRouters(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -37,7 +37,7 @@ func Test_GetAllRoutersInNetworkReturnsRouters(t *testing.T) {
|
||||
func Test_GetAllRoutersInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
|
||||
s, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../../testdata/networks.sql", t.TempDir())
|
||||
@@ -45,7 +45,7 @@ func Test_GetAllRoutersInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -58,7 +58,7 @@ func Test_GetAllRoutersInNetworkReturnsPermissionDenied(t *testing.T) {
|
||||
func Test_GetRouterReturnsRouter(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testRouterId"
|
||||
|
||||
@@ -67,7 +67,7 @@ func Test_GetRouterReturnsRouter(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -79,7 +79,7 @@ func Test_GetRouterReturnsRouter(t *testing.T) {
|
||||
func Test_GetRouterReturnsPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
resourceID := "testRouterId"
|
||||
|
||||
@@ -88,7 +88,7 @@ func Test_GetRouterReturnsPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -100,7 +100,7 @@ func Test_GetRouterReturnsPermissionDenied(t *testing.T) {
|
||||
|
||||
func Test_CreateRouterSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
router, err := types.NewNetworkRouter("testAccountId", "testNetworkId", "testPeerId", []string{}, false, 9999, true)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
@@ -111,7 +111,7 @@ func Test_CreateRouterSuccessfully(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -126,7 +126,7 @@ func Test_CreateRouterSuccessfully(t *testing.T) {
|
||||
|
||||
func Test_CreateRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
router, err := types.NewNetworkRouter("testAccountId", "testNetworkId", "testPeerId", []string{}, false, 9999, true)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
@@ -137,7 +137,7 @@ func Test_CreateRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -150,7 +150,7 @@ func Test_CreateRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
func Test_DeleteRouterSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
networkID := "testNetworkId"
|
||||
routerID := "testRouterId"
|
||||
|
||||
@@ -159,7 +159,7 @@ func Test_DeleteRouterSuccessfully(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -170,7 +170,7 @@ func Test_DeleteRouterSuccessfully(t *testing.T) {
|
||||
func Test_DeleteRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
accountID := "testAccountId"
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
networkID := "testNetworkId"
|
||||
routerID := "testRouterId"
|
||||
|
||||
@@ -179,7 +179,7 @@ func Test_DeleteRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -190,7 +190,7 @@ func Test_DeleteRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
|
||||
func Test_UpdateRouterSuccessfully(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "allowedUser"
|
||||
userID := "testAdminId"
|
||||
router, err := types.NewNetworkRouter("testAccountId", "testNetworkId", "testPeerId", []string{}, false, 1, true)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
@@ -201,7 +201,7 @@ func Test_UpdateRouterSuccessfully(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
@@ -212,7 +212,7 @@ func Test_UpdateRouterSuccessfully(t *testing.T) {
|
||||
|
||||
func Test_UpdateRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userID := "invalidUser"
|
||||
userID := "testUserId"
|
||||
router, err := types.NewNetworkRouter("testAccountId", "testNetworkId", "testPeerId", []string{}, false, 1, true)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
@@ -223,7 +223,7 @@ func Test_UpdateRouterFailsWithPermissionDenied(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(cleanUp)
|
||||
permissionsManager := permissions.NewManagerMock()
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
am := mock_server.MockAccountManager{}
|
||||
manager := NewManager(s, permissionsManager, &am)
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/management/domain"
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/idp"
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
@@ -37,17 +39,9 @@ func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() && settings.RegularUsersViewBlocked {
|
||||
return []*nbpeer.Peer{}, nil
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
accountPeers, err := am.Store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, nameFilter, ipFilter)
|
||||
@@ -67,10 +61,23 @@ func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID
|
||||
peersMap[peer.ID] = peer
|
||||
}
|
||||
|
||||
if user.IsAdminOrServiceUser() {
|
||||
if allowed {
|
||||
return peers, nil
|
||||
}
|
||||
|
||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get account settings: %w", err)
|
||||
}
|
||||
|
||||
if settings.RegularUsersViewBlocked {
|
||||
return []*nbpeer.Peer{}, nil
|
||||
}
|
||||
|
||||
return am.getUserAccessiblePeers(ctx, accountID, peersMap, peers)
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) getUserAccessiblePeers(ctx context.Context, accountID string, peersMap map[string]*nbpeer.Peer, peers []*nbpeer.Peer) ([]*nbpeer.Peer, error) {
|
||||
account, err := am.requestBuffer.GetAccountWithBackpressure(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -135,7 +142,7 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK
|
||||
if expired {
|
||||
// we need to update other peers because when peer login expires all other peers are notified to disconnect from
|
||||
// the expired one. Here we notify them that connection is now allowed again.
|
||||
am.UpdateAccountPeers(ctx, accountID)
|
||||
am.BufferUpdateAccountPeers(ctx, accountID)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -183,13 +190,12 @@ func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, user
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Update)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var peer *nbpeer.Peer
|
||||
@@ -315,15 +321,12 @@ func (am *DefaultAccountManager) DeletePeer(ctx context.Context, accountID, peer
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
if userID != activity.SystemInitiator {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Delete)
|
||||
if err != nil {
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
peerAccountID, err := am.Store.GetAccountIDByPeerID(ctx, store.LockingStrengthShare, peerID)
|
||||
@@ -383,7 +386,7 @@ func (am *DefaultAccountManager) DeletePeer(ctx context.Context, accountID, peer
|
||||
}
|
||||
|
||||
if updateAccountPeers {
|
||||
am.UpdateAccountPeers(ctx, accountID)
|
||||
am.BufferUpdateAccountPeers(ctx, accountID)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -653,7 +656,7 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s
|
||||
unlock = nil
|
||||
|
||||
if updateAccountPeers {
|
||||
am.UpdateAccountPeers(ctx, accountID)
|
||||
am.BufferUpdateAccountPeers(ctx, accountID)
|
||||
}
|
||||
|
||||
return am.getValidatedPeerWithMap(ctx, false, accountID, newPeer)
|
||||
@@ -748,7 +751,7 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSy
|
||||
}
|
||||
|
||||
if isStatusChanged || sync.UpdateAccountPeers || (updated && len(postureChecks) > 0) {
|
||||
am.UpdateAccountPeers(ctx, accountID)
|
||||
am.BufferUpdateAccountPeers(ctx, accountID)
|
||||
}
|
||||
|
||||
return am.getValidatedPeerWithMap(ctx, peerNotValid, accountID, peer)
|
||||
@@ -893,7 +896,7 @@ func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login types.Peer
|
||||
unlockPeer = nil
|
||||
|
||||
if updateRemotePeers || isStatusChanged || (isPeerUpdated && len(postureChecks) > 0) {
|
||||
am.UpdateAccountPeers(ctx, accountID)
|
||||
am.BufferUpdateAccountPeers(ctx, accountID)
|
||||
}
|
||||
|
||||
return am.getValidatedPeerWithMap(ctx, isRequiresApproval, accountID, peer)
|
||||
@@ -1094,41 +1097,33 @@ func peerLoginExpired(ctx context.Context, peer *nbpeer.Peer, settings *types.Se
|
||||
|
||||
// GetPeer for a given accountID, peerID and userID error if not found.
|
||||
func (am *DefaultAccountManager) GetPeer(ctx context.Context, accountID, peerID, userID string) (*nbpeer.Peer, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() && settings.RegularUsersViewBlocked {
|
||||
return nil, status.Errorf(status.Internal, "user %s has no access to his own peer %s under account %s", userID, peerID, accountID)
|
||||
}
|
||||
|
||||
peer, err := am.Store.GetPeerByID(ctx, store.LockingStrengthShare, accountID, peerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
if allowed {
|
||||
return peer, nil
|
||||
}
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if admin or user owns this peer, return peer
|
||||
if user.IsAdminOrServiceUser() || peer.UserID == userID {
|
||||
return peer, nil
|
||||
}
|
||||
|
||||
// it is also possible that user doesn't own the peer but some of his peers have access to it,
|
||||
// this is a valid case, show the peer as well.
|
||||
userPeers, err := am.Store.GetUserPeers(ctx, store.LockingStrengthShare, accountID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return am.checkIfUserOwnsPeer(ctx, accountID, userID, peer)
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) checkIfUserOwnsPeer(ctx context.Context, accountID, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, error) {
|
||||
account, err := am.requestBuffer.GetAccountWithBackpressure(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1139,16 +1134,23 @@ func (am *DefaultAccountManager) GetPeer(ctx context.Context, accountID, peerID,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it is also possible that user doesn't own the peer but some of his peers have access to it,
|
||||
// this is a valid case, show the peer as well.
|
||||
userPeers, err := am.Store.GetUserPeers(ctx, store.LockingStrengthShare, accountID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, p := range userPeers {
|
||||
aclPeers, _ := account.GetPeerConnectionResources(ctx, p.ID, approvedPeersMap)
|
||||
for _, aclPeer := range aclPeers {
|
||||
if aclPeer.ID == peerID {
|
||||
if aclPeer.ID == peer.ID {
|
||||
return peer, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, status.Errorf(status.Internal, "user %s has no access to peer %s under account %s", userID, peerID, accountID)
|
||||
return nil, status.Errorf(status.Internal, "user %s has no access to peer %s under account %s", userID, peer.ID, accountID)
|
||||
}
|
||||
|
||||
// UpdateAccountPeers updates all peers that belong to an account.
|
||||
@@ -1226,6 +1228,21 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
|
||||
}
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) BufferUpdateAccountPeers(ctx context.Context, accountID string) {
|
||||
mu, _ := am.accountUpdateLocks.LoadOrStore(accountID, &sync.Mutex{})
|
||||
lock := mu.(*sync.Mutex)
|
||||
|
||||
if !lock.TryLock() {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Duration(am.updateAccountPeersBufferInterval.Load()))
|
||||
lock.Unlock()
|
||||
am.UpdateAccountPeers(ctx, accountID)
|
||||
}()
|
||||
}
|
||||
|
||||
// UpdateAccountPeer updates a single peer that belongs to an account.
|
||||
// Should be called when changes need to be synced to a specific peer only.
|
||||
func (am *DefaultAccountManager) UpdateAccountPeer(ctx context.Context, accountId string, peerId string) {
|
||||
|
||||
@@ -303,12 +303,12 @@ func TestAccountManager_GetNetworkMapWithPolicy(t *testing.T) {
|
||||
group1.Peers = append(group1.Peers, peer1.ID)
|
||||
group2.Peers = append(group2.Peers, peer2.ID)
|
||||
|
||||
err = manager.SaveGroup(context.Background(), account.Id, userID, &group1)
|
||||
err = manager.SaveGroup(context.Background(), account.Id, userID, &group1, true)
|
||||
if err != nil {
|
||||
t.Errorf("expecting group1 to be added, got failure %v", err)
|
||||
return
|
||||
}
|
||||
err = manager.SaveGroup(context.Background(), account.Id, userID, &group2)
|
||||
err = manager.SaveGroup(context.Background(), account.Id, userID, &group2, true)
|
||||
if err != nil {
|
||||
t.Errorf("expecting group2 to be added, got failure %v", err)
|
||||
return
|
||||
@@ -327,7 +327,7 @@ func TestAccountManager_GetNetworkMapWithPolicy(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
policy, err = manager.SavePolicy(context.Background(), account.Id, userID, policy)
|
||||
policy, err = manager.SavePolicy(context.Background(), account.Id, userID, policy, true)
|
||||
if err != nil {
|
||||
t.Errorf("expecting rule to be added, got failure %v", err)
|
||||
return
|
||||
@@ -375,7 +375,7 @@ func TestAccountManager_GetNetworkMapWithPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
policy.Enabled = false
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, userID, policy)
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, userID, policy, true)
|
||||
if err != nil {
|
||||
t.Errorf("expecting rule to be added, got failure %v", err)
|
||||
return
|
||||
@@ -1264,9 +1264,9 @@ func Test_RegisterPeerByUser(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
@@ -1333,9 +1333,9 @@ func Test_RegisterPeerBySetupKey(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
@@ -1406,8 +1406,9 @@ func Test_RegisterPeerRollbackOnFailure(t *testing.T) {
|
||||
t.Cleanup(ctrl.Finish)
|
||||
settingsMockManager := settings.NewMockManager(ctrl)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
permissionsManager := permissions.NewManager(s)
|
||||
|
||||
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
assert.NoError(t, err)
|
||||
|
||||
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
@@ -1477,7 +1478,7 @@ func TestPeerAccountPeersUpdate(t *testing.T) {
|
||||
Name: "GroupC",
|
||||
Peers: []string{},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
// create a user with auto groups
|
||||
@@ -1653,7 +1654,7 @@ func TestPeerAccountPeersUpdate(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
)
|
||||
@@ -31,7 +33,7 @@ func NewManager(store store.Store, permissionsManager permissions.Manager) Manag
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error) {
|
||||
allowed, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Peers, permissions.Read)
|
||||
allowed, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Read)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate user permissions: %w", err)
|
||||
}
|
||||
@@ -44,13 +46,13 @@ func (m *managerImpl) GetPeer(ctx context.Context, accountID, userID, peerID str
|
||||
}
|
||||
|
||||
func (m *managerImpl) GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error) {
|
||||
allowed, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Peers, permissions.Read)
|
||||
allowed, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Read)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate user permissions: %w", err)
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
return m.store.GetUserPeers(ctx, store.LockingStrengthShare, accountID, userID)
|
||||
}
|
||||
|
||||
return m.store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
|
||||
|
||||
@@ -1,34 +1,24 @@
|
||||
package permissions
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/roles"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
type Module string
|
||||
|
||||
const (
|
||||
Networks Module = "networks"
|
||||
Peers Module = "peers"
|
||||
Groups Module = "groups"
|
||||
Settings Module = "settings"
|
||||
Accounts Module = "accounts"
|
||||
)
|
||||
|
||||
type Operation string
|
||||
|
||||
const (
|
||||
Read Operation = "read"
|
||||
Write Operation = "write"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
ValidateUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error)
|
||||
ValidateUserPermissions(ctx context.Context, accountID, userID string, module modules.Module, operation operations.Operation) (bool, error)
|
||||
ValidateRoleModuleAccess(ctx context.Context, accountID string, role roles.RolePermissions, module modules.Module, operation operations.Operation) bool
|
||||
ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error
|
||||
}
|
||||
|
||||
@@ -36,16 +26,23 @@ type managerImpl struct {
|
||||
store store.Store
|
||||
}
|
||||
|
||||
type managerMock struct {
|
||||
}
|
||||
|
||||
func NewManager(store store.Store) Manager {
|
||||
return &managerImpl{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerImpl) ValidateUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error) {
|
||||
func (m *managerImpl) ValidateUserPermissions(
|
||||
ctx context.Context,
|
||||
accountID string,
|
||||
userID string,
|
||||
module modules.Module,
|
||||
operation operations.Operation,
|
||||
) (bool, error) {
|
||||
if userID == activity.SystemInitiator {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
user, err := m.store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -55,49 +52,42 @@ func (m *managerImpl) ValidateUserPermissions(ctx context.Context, accountID, us
|
||||
return false, status.NewUserNotFoundError(userID)
|
||||
}
|
||||
|
||||
if user.IsBlocked() {
|
||||
return false, status.NewUserBlockedError()
|
||||
}
|
||||
|
||||
if err := m.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch module {
|
||||
case Accounts:
|
||||
if operation == Write && user.Role != types.UserRoleOwner {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
default:
|
||||
if operation == operations.Read && user.IsServiceUser {
|
||||
return true, nil // this should be replaced by proper granular access role
|
||||
}
|
||||
|
||||
switch user.Role {
|
||||
case types.UserRoleAdmin, types.UserRoleOwner:
|
||||
return true, nil
|
||||
case types.UserRoleUser:
|
||||
return m.validateRegularUserPermissions(ctx, accountID, module, operation)
|
||||
case types.UserRoleBillingAdmin:
|
||||
return false, nil
|
||||
default:
|
||||
return false, errors.New("invalid role")
|
||||
role, ok := roles.RolesMap[user.Role]
|
||||
if !ok {
|
||||
return false, status.NewUserRoleNotFoundError(string(user.Role))
|
||||
}
|
||||
|
||||
return m.ValidateRoleModuleAccess(ctx, accountID, role, module, operation), nil
|
||||
}
|
||||
|
||||
func (m *managerImpl) validateRegularUserPermissions(ctx context.Context, accountID string, module Module, operation Operation) (bool, error) {
|
||||
settings, err := m.store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get settings: %w", err)
|
||||
}
|
||||
if settings.RegularUsersViewBlocked {
|
||||
return false, nil
|
||||
func (m *managerImpl) ValidateRoleModuleAccess(
|
||||
ctx context.Context,
|
||||
accountID string,
|
||||
role roles.RolePermissions,
|
||||
module modules.Module,
|
||||
operation operations.Operation,
|
||||
) bool {
|
||||
if permissions, ok := role.Permissions[module]; ok {
|
||||
if allowed, exists := permissions[operation]; exists {
|
||||
return allowed
|
||||
}
|
||||
log.WithContext(ctx).Tracef("operation %s not found on module %s for role %s", operation, module, role.Role)
|
||||
return false
|
||||
}
|
||||
|
||||
if operation == Write {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if module == Peers {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
return role.AutoAllowNew[operation]
|
||||
}
|
||||
|
||||
func (m *managerImpl) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error {
|
||||
@@ -106,24 +96,3 @@ func (m *managerImpl) ValidateAccountAccess(ctx context.Context, accountID strin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewManagerMock() Manager {
|
||||
return &managerMock{}
|
||||
}
|
||||
|
||||
func (m *managerMock) ValidateUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error) {
|
||||
switch userID {
|
||||
case "a23efe53-63fb-11ec-90d6-0242ac120003", "allowedUser", "testingUser", "account_creator", "serviceUserID", "test_user":
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerMock) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error {
|
||||
// @note managers explicitly checked this, so should the mock
|
||||
if user.AccountID != accountID {
|
||||
return status.NewUserNotPartOfAccountError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
82
management/server/permissions/manager_mock.go
Normal file
82
management/server/permissions/manager_mock.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: ./manager.go
|
||||
|
||||
// Package permissions is a generated GoMock package.
|
||||
package permissions
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
modules "github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
operations "github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
roles "github.com/netbirdio/netbird/management/server/permissions/roles"
|
||||
types "github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// MockManager is a mock of Manager interface.
|
||||
type MockManager struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockManagerMockRecorder
|
||||
}
|
||||
|
||||
// MockManagerMockRecorder is the mock recorder for MockManager.
|
||||
type MockManagerMockRecorder struct {
|
||||
mock *MockManager
|
||||
}
|
||||
|
||||
// NewMockManager creates a new mock instance.
|
||||
func NewMockManager(ctrl *gomock.Controller) *MockManager {
|
||||
mock := &MockManager{ctrl: ctrl}
|
||||
mock.recorder = &MockManagerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockManager) EXPECT() *MockManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// ValidateAccountAccess mocks base method.
|
||||
func (m *MockManager) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User, allowOwnerAndAdmin bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateAccountAccess", ctx, accountID, user, allowOwnerAndAdmin)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ValidateAccountAccess indicates an expected call of ValidateAccountAccess.
|
||||
func (mr *MockManagerMockRecorder) ValidateAccountAccess(ctx, accountID, user, allowOwnerAndAdmin interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAccountAccess", reflect.TypeOf((*MockManager)(nil).ValidateAccountAccess), ctx, accountID, user, allowOwnerAndAdmin)
|
||||
}
|
||||
|
||||
// ValidateRoleModuleAccess mocks base method.
|
||||
func (m *MockManager) ValidateRoleModuleAccess(ctx context.Context, accountID string, role roles.RolePermissions, module modules.Module, operation operations.Operation) bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateRoleModuleAccess", ctx, accountID, role, module, operation)
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ValidateRoleModuleAccess indicates an expected call of ValidateRoleModuleAccess.
|
||||
func (mr *MockManagerMockRecorder) ValidateRoleModuleAccess(ctx, accountID, role, module, operation interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRoleModuleAccess", reflect.TypeOf((*MockManager)(nil).ValidateRoleModuleAccess), ctx, accountID, role, module, operation)
|
||||
}
|
||||
|
||||
// ValidateUserPermissions mocks base method.
|
||||
func (m *MockManager) ValidateUserPermissions(ctx context.Context, accountID, userID string, module modules.Module, operation operations.Operation) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateUserPermissions", ctx, accountID, userID, module, operation)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ValidateUserPermissions indicates an expected call of ValidateUserPermissions.
|
||||
func (mr *MockManagerMockRecorder) ValidateUserPermissions(ctx, accountID, userID, module, operation interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUserPermissions", reflect.TypeOf((*MockManager)(nil).ValidateUserPermissions), ctx, accountID, userID, module, operation)
|
||||
}
|
||||
19
management/server/permissions/modules/module.go
Normal file
19
management/server/permissions/modules/module.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package modules
|
||||
|
||||
type Module string
|
||||
|
||||
const (
|
||||
Networks Module = "networks"
|
||||
Peers Module = "peers"
|
||||
Groups Module = "groups"
|
||||
Settings Module = "settings"
|
||||
Accounts Module = "accounts"
|
||||
Dns Module = "dns"
|
||||
Nameservers Module = "nameservers"
|
||||
Events Module = "events"
|
||||
Policies Module = "policies"
|
||||
Routes Module = "routes"
|
||||
Users Module = "users"
|
||||
SetupKeys Module = "setup_keys"
|
||||
Pats Module = "pats"
|
||||
)
|
||||
10
management/server/permissions/operations/operation.go
Normal file
10
management/server/permissions/operations/operation.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package operations
|
||||
|
||||
type Operation string
|
||||
|
||||
const (
|
||||
Create Operation = "create"
|
||||
Read Operation = "read"
|
||||
Update Operation = "update"
|
||||
Delete Operation = "delete"
|
||||
)
|
||||
25
management/server/permissions/roles/admin.go
Normal file
25
management/server/permissions/roles/admin.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
var Admin = RolePermissions{
|
||||
Role: types.UserRoleAdmin,
|
||||
AutoAllowNew: map[operations.Operation]bool{
|
||||
operations.Read: true,
|
||||
operations.Create: true,
|
||||
operations.Update: true,
|
||||
operations.Delete: true,
|
||||
},
|
||||
Permissions: Permissions{
|
||||
modules.Accounts: {
|
||||
operations.Read: true,
|
||||
operations.Create: false,
|
||||
operations.Update: false,
|
||||
operations.Delete: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
16
management/server/permissions/roles/owner.go
Normal file
16
management/server/permissions/roles/owner.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
var Owner = RolePermissions{
|
||||
Role: types.UserRoleOwner,
|
||||
AutoAllowNew: map[operations.Operation]bool{
|
||||
operations.Read: true,
|
||||
operations.Create: true,
|
||||
operations.Update: true,
|
||||
operations.Delete: true,
|
||||
},
|
||||
}
|
||||
21
management/server/permissions/roles/role_permissions.go
Normal file
21
management/server/permissions/roles/role_permissions.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
type RolePermissions struct {
|
||||
Role types.UserRole
|
||||
Permissions Permissions
|
||||
AutoAllowNew map[operations.Operation]bool
|
||||
}
|
||||
|
||||
type Permissions map[modules.Module]map[operations.Operation]bool
|
||||
|
||||
var RolesMap = map[types.UserRole]RolePermissions{
|
||||
types.UserRoleOwner: Owner,
|
||||
types.UserRoleAdmin: Admin,
|
||||
types.UserRoleUser: User,
|
||||
}
|
||||
16
management/server/permissions/roles/user.go
Normal file
16
management/server/permissions/roles/user.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package roles
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
var User = RolePermissions{
|
||||
Role: types.UserRoleUser,
|
||||
AutoAllowNew: map[operations.Operation]bool{
|
||||
operations.Read: false,
|
||||
operations.Create: false,
|
||||
operations.Update: false,
|
||||
operations.Delete: false,
|
||||
},
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/rs/xid"
|
||||
|
||||
"github.com/netbirdio/netbird/management/proto"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
@@ -17,38 +19,32 @@ import (
|
||||
|
||||
// GetPolicy from the store
|
||||
func (am *DefaultAccountManager) GetPolicy(ctx context.Context, accountID, policyID, userID string) (*types.Policy, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetPolicyByID(ctx, store.LockingStrengthShare, accountID, policyID)
|
||||
}
|
||||
|
||||
// SavePolicy in the store
|
||||
func (am *DefaultAccountManager) SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy) (*types.Policy, error) {
|
||||
func (am *DefaultAccountManager) SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy, create bool) (*types.Policy, error) {
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
operation := operations.Create
|
||||
if !create {
|
||||
operation = operations.Update
|
||||
}
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var isUpdate = policy.ID != ""
|
||||
@@ -95,17 +91,12 @@ func (am *DefaultAccountManager) DeletePolicy(ctx context.Context, accountID, po
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operations.Delete)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var policy *types.Policy
|
||||
@@ -143,17 +134,12 @@ func (am *DefaultAccountManager) DeletePolicy(ctx context.Context, accountID, po
|
||||
|
||||
// ListPolicies from the store.
|
||||
func (am *DefaultAccountManager) ListPolicies(ctx context.Context, accountID, userID string) ([]*types.Policy, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetAccountPolicies(ctx, store.LockingStrengthShare, accountID)
|
||||
|
||||
@@ -883,7 +883,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
Name: "GroupD",
|
||||
Peers: []string{peer1.ID, peer2.ID},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
|
||||
@@ -915,7 +915,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -947,7 +947,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -979,7 +979,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -1010,7 +1010,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -1030,7 +1030,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
}()
|
||||
|
||||
policyWithSourceAndDestinationPeers.Enabled = false
|
||||
policyWithSourceAndDestinationPeers, err = manager.SavePolicy(context.Background(), account.Id, userID, policyWithSourceAndDestinationPeers)
|
||||
policyWithSourceAndDestinationPeers, err = manager.SavePolicy(context.Background(), account.Id, userID, policyWithSourceAndDestinationPeers, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -1051,7 +1051,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
|
||||
policyWithSourceAndDestinationPeers.Description = "updated description"
|
||||
policyWithSourceAndDestinationPeers.Rules[0].Destinations = []string{"groupA"}
|
||||
policyWithSourceAndDestinationPeers, err = manager.SavePolicy(context.Background(), account.Id, userID, policyWithSourceAndDestinationPeers)
|
||||
policyWithSourceAndDestinationPeers, err = manager.SavePolicy(context.Background(), account.Id, userID, policyWithSourceAndDestinationPeers, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -1071,7 +1071,7 @@ func TestPolicyAccountPeersUpdate(t *testing.T) {
|
||||
}()
|
||||
|
||||
policyWithSourceAndDestinationPeers.Enabled = true
|
||||
policyWithSourceAndDestinationPeers, err = manager.SavePolicy(context.Background(), account.Id, userID, policyWithSourceAndDestinationPeers)
|
||||
policyWithSourceAndDestinationPeers, err = manager.SavePolicy(context.Background(), account.Id, userID, policyWithSourceAndDestinationPeers, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
@@ -17,38 +19,32 @@ import (
|
||||
)
|
||||
|
||||
func (am *DefaultAccountManager) GetPostureChecks(ctx context.Context, accountID, postureChecksID, userID string) (*posture.Checks, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetPostureChecksByID(ctx, store.LockingStrengthShare, accountID, postureChecksID)
|
||||
}
|
||||
|
||||
// SavePostureChecks saves a posture check.
|
||||
func (am *DefaultAccountManager) SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error) {
|
||||
func (am *DefaultAccountManager) SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks, create bool) (*posture.Checks, error) {
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
operation := operations.Create
|
||||
if !create {
|
||||
operation = operations.Update
|
||||
}
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var updateAccountPeers bool
|
||||
@@ -94,17 +90,12 @@ func (am *DefaultAccountManager) DeletePostureChecks(ctx context.Context, accoun
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Routes, operations.Read)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() {
|
||||
return status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var postureChecks *posture.Checks
|
||||
@@ -136,17 +127,12 @@ func (am *DefaultAccountManager) DeletePostureChecks(ctx context.Context, accoun
|
||||
|
||||
// ListPostureChecks returns a list of posture checks.
|
||||
func (am *DefaultAccountManager) ListPostureChecks(ctx context.Context, accountID, userID string) ([]*posture.Checks, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Policies, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !user.HasAdminPower() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetAccountPostureChecks(ctx, store.LockingStrengthShare, accountID)
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestDefaultAccountManager_PostureCheck(t *testing.T) {
|
||||
|
||||
t.Run("Generic posture check flow", func(t *testing.T) {
|
||||
// regular users can not create checks
|
||||
_, err = am.SavePostureChecks(context.Background(), account.Id, regularUserID, &posture.Checks{})
|
||||
_, err = am.SavePostureChecks(context.Background(), account.Id, regularUserID, &posture.Checks{}, true)
|
||||
assert.Error(t, err)
|
||||
|
||||
// regular users cannot list check
|
||||
@@ -48,7 +48,7 @@ func TestDefaultAccountManager_PostureCheck(t *testing.T) {
|
||||
MinVersion: "0.26.0",
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// admin users can list check
|
||||
@@ -68,7 +68,7 @@ func TestDefaultAccountManager_PostureCheck(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.Error(t, err)
|
||||
|
||||
// admins can update posture checks
|
||||
@@ -77,7 +77,7 @@ func TestDefaultAccountManager_PostureCheck(t *testing.T) {
|
||||
MinVersion: "0.27.0",
|
||||
},
|
||||
}
|
||||
_, err = am.SavePostureChecks(context.Background(), account.Id, adminUserID, postureCheck)
|
||||
_, err = am.SavePostureChecks(context.Background(), account.Id, adminUserID, postureCheck, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// users should not be able to delete posture checks
|
||||
@@ -137,7 +137,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
Name: "GroupC",
|
||||
Peers: []string{},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
|
||||
@@ -156,7 +156,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
postureCheckA, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckA)
|
||||
postureCheckA, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckA, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
postureCheckB := &posture.Checks{
|
||||
@@ -177,7 +177,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
close(done)
|
||||
}()
|
||||
|
||||
postureCheckB, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB)
|
||||
postureCheckB, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -200,7 +200,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
MinVersion: "0.29.0",
|
||||
},
|
||||
}
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB)
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -232,7 +232,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
close(done)
|
||||
}()
|
||||
|
||||
policy, err = manager.SavePolicy(context.Background(), account.Id, userID, policy)
|
||||
policy, err = manager.SavePolicy(context.Background(), account.Id, userID, policy, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -261,7 +261,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
close(done)
|
||||
}()
|
||||
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB)
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -280,7 +280,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
}()
|
||||
|
||||
policy.SourcePostureChecks = []string{}
|
||||
_, err := manager.SavePolicy(context.Background(), account.Id, userID, policy)
|
||||
_, err := manager.SavePolicy(context.Background(), account.Id, userID, policy, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -308,7 +308,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB)
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Updating linked posture check to policy with no peers should not trigger account peers update and not send peer update
|
||||
@@ -325,7 +325,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
SourcePostureChecks: []string{postureCheckB.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
done := make(chan struct{})
|
||||
@@ -339,7 +339,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
MinVersion: "0.29.0",
|
||||
},
|
||||
}
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB)
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -369,7 +369,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
SourcePostureChecks: []string{postureCheckB.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
done := make(chan struct{})
|
||||
@@ -383,7 +383,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
MinVersion: "0.29.0",
|
||||
},
|
||||
}
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB)
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -408,7 +408,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
SourcePostureChecks: []string{postureCheckB.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
done := make(chan struct{})
|
||||
@@ -426,7 +426,7 @@ func TestPostureCheckAccountPeersUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB)
|
||||
_, err = manager.SavePostureChecks(context.Background(), account.Id, userID, postureCheckB, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -465,7 +465,7 @@ func TestArePostureCheckChangesAffectPeers(t *testing.T) {
|
||||
NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.33.1"},
|
||||
},
|
||||
}
|
||||
postureCheckA, err = manager.SavePostureChecks(context.Background(), account.Id, adminUserID, postureCheckA)
|
||||
postureCheckA, err = manager.SavePostureChecks(context.Background(), account.Id, adminUserID, postureCheckA, true)
|
||||
require.NoError(t, err, "failed to save postureCheckA")
|
||||
|
||||
postureCheckB := &posture.Checks{
|
||||
@@ -475,7 +475,7 @@ func TestArePostureCheckChangesAffectPeers(t *testing.T) {
|
||||
NBVersionCheck: &posture.NBVersionCheck{MinVersion: "0.33.1"},
|
||||
},
|
||||
}
|
||||
postureCheckB, err = manager.SavePostureChecks(context.Background(), account.Id, adminUserID, postureCheckB)
|
||||
postureCheckB, err = manager.SavePostureChecks(context.Background(), account.Id, adminUserID, postureCheckB, true)
|
||||
require.NoError(t, err, "failed to save postureCheckB")
|
||||
|
||||
policy := &types.Policy{
|
||||
@@ -490,7 +490,7 @@ func TestArePostureCheckChangesAffectPeers(t *testing.T) {
|
||||
SourcePostureChecks: []string{postureCheckA.ID},
|
||||
}
|
||||
|
||||
policy, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy)
|
||||
policy, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy, true)
|
||||
require.NoError(t, err, "failed to save policy")
|
||||
|
||||
t.Run("posture check exists and is linked to policy with peers", func(t *testing.T) {
|
||||
@@ -514,7 +514,7 @@ func TestArePostureCheckChangesAffectPeers(t *testing.T) {
|
||||
t.Run("posture check is linked to policy with no peers in source groups", func(t *testing.T) {
|
||||
policy.Rules[0].Sources = []string{"groupB"}
|
||||
policy.Rules[0].Destinations = []string{"groupA"}
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy)
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy, true)
|
||||
require.NoError(t, err, "failed to update policy")
|
||||
|
||||
result, err := arePostureCheckChangesAffectPeers(context.Background(), manager.Store, account.Id, postureCheckA.ID)
|
||||
@@ -525,7 +525,7 @@ func TestArePostureCheckChangesAffectPeers(t *testing.T) {
|
||||
t.Run("posture check is linked to policy with no peers in destination groups", func(t *testing.T) {
|
||||
policy.Rules[0].Sources = []string{"groupA"}
|
||||
policy.Rules[0].Destinations = []string{"groupB"}
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy)
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy, true)
|
||||
require.NoError(t, err, "failed to update policy")
|
||||
|
||||
result, err := arePostureCheckChangesAffectPeers(context.Background(), manager.Store, account.Id, postureCheckA.ID)
|
||||
@@ -546,7 +546,7 @@ func TestArePostureCheckChangesAffectPeers(t *testing.T) {
|
||||
t.Run("posture check is linked to policy with non-existent group", func(t *testing.T) {
|
||||
policy.Rules[0].Sources = []string{"nonExistentGroup"}
|
||||
policy.Rules[0].Destinations = []string{"nonExistentGroup"}
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy)
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, adminUserID, policy, true)
|
||||
require.NoError(t, err, "failed to update policy")
|
||||
|
||||
result, err := arePostureCheckChangesAffectPeers(context.Background(), manager.Store, account.Id, postureCheckA.ID)
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/rs/xid"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
@@ -20,17 +22,12 @@ import (
|
||||
|
||||
// GetRoute gets a route object from account and route IDs
|
||||
func (am *DefaultAccountManager) GetRoute(ctx context.Context, accountID string, routeID route.ID, userID string) (*route.Route, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Routes, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !user.IsAdminOrServiceUser() {
|
||||
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view Network Routes")
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetRouteByID(ctx, store.LockingStrengthShare, string(routeID), accountID)
|
||||
@@ -123,13 +120,12 @@ func (am *DefaultAccountManager) CreateRoute(ctx context.Context, accountID stri
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Routes, operations.Create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err = am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
account, err := am.Store.GetAccount(ctx, accountID)
|
||||
@@ -242,13 +238,12 @@ func (am *DefaultAccountManager) SaveRoute(ctx context.Context, accountID, userI
|
||||
return status.Errorf(status.InvalidArgument, "identifier should be between 1 and %d", route.MaxNetIDChar)
|
||||
}
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Routes, operations.Update)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err = am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
account, err := am.Store.GetAccount(ctx, accountID)
|
||||
@@ -318,13 +313,12 @@ func (am *DefaultAccountManager) DeleteRoute(ctx context.Context, accountID stri
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Routes, operations.Delete)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err = am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
account, err := am.Store.GetAccount(ctx, accountID)
|
||||
@@ -354,17 +348,12 @@ func (am *DefaultAccountManager) DeleteRoute(ctx context.Context, accountID stri
|
||||
|
||||
// ListRoutes returns a list of routes from account
|
||||
func (am *DefaultAccountManager) ListRoutes(ctx context.Context, accountID, userID string) ([]*route.Route, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Routes, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !user.IsAdminOrServiceUser() {
|
||||
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view Network Routes")
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetAccountRoutes(ctx, store.LockingStrengthShare, accountID)
|
||||
|
||||
@@ -1215,7 +1215,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
|
||||
Name: "peer1 group",
|
||||
Peers: []string{peer1ID},
|
||||
}
|
||||
err = am.SaveGroup(context.Background(), account.Id, userID, newGroup)
|
||||
err = am.SaveGroup(context.Background(), account.Id, userID, newGroup, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
rules, err := am.ListPolicies(context.Background(), account.Id, "testingUser")
|
||||
@@ -1227,7 +1227,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
|
||||
newPolicy.Rules[0].Sources = []string{newGroup.ID}
|
||||
newPolicy.Rules[0].Destinations = []string{newGroup.ID}
|
||||
|
||||
_, err = am.SavePolicy(context.Background(), account.Id, userID, newPolicy)
|
||||
_, err = am.SavePolicy(context.Background(), account.Id, userID, newPolicy, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = am.DeletePolicy(context.Background(), account.Id, defaultRule.ID, userID)
|
||||
@@ -1260,7 +1260,6 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
permissionsManagerMock := permissions.NewManagerMock()
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
|
||||
@@ -1283,7 +1282,9 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||
AnyTimes().
|
||||
Return(&types.ExtraSettings{}, nil)
|
||||
|
||||
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
|
||||
permissionsManager := permissions.NewManager(store)
|
||||
|
||||
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager)
|
||||
}
|
||||
|
||||
func createRouterStore(t *testing.T) (store.Store, error) {
|
||||
@@ -1504,7 +1505,7 @@ func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*types.Accou
|
||||
}
|
||||
|
||||
for _, group := range newGroup {
|
||||
err = am.SaveGroup(context.Background(), accountID, userID, group)
|
||||
err = am.SaveGroup(context.Background(), accountID, userID, group, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1958,7 +1959,7 @@ func TestRouteAccountPeersUpdate(t *testing.T) {
|
||||
Name: "GroupC",
|
||||
Peers: []string{},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1ID)
|
||||
@@ -2142,7 +2143,7 @@ func TestRouteAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupB",
|
||||
Name: "GroupB",
|
||||
Peers: []string{peer1ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
@@ -2182,7 +2183,7 @@ func TestRouteAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupC",
|
||||
Name: "GroupC",
|
||||
Peers: []string{peer1ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/integrations/extra_settings"
|
||||
"github.com/netbirdio/netbird/management/server/permissions"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -44,7 +46,7 @@ func (m *managerImpl) GetExtraSettingsManager() extra_settings.Manager {
|
||||
|
||||
func (m *managerImpl) GetSettings(ctx context.Context, accountID, userID string) (*types.Settings, error) {
|
||||
if userID != activity.SystemInitiator {
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Settings, permissions.Read)
|
||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Settings, operations.Read)
|
||||
if err != nil {
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
@@ -56,17 +58,12 @@ func (am *DefaultAccountManager) CreateSetupKey(ctx context.Context, accountID s
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.SetupKeys, operations.Create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var setupKey *types.SetupKey
|
||||
@@ -113,17 +110,12 @@ func (am *DefaultAccountManager) SaveSetupKey(ctx context.Context, accountID str
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.SetupKeys, operations.Update)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var oldKey *types.SetupKey
|
||||
@@ -175,17 +167,12 @@ func (am *DefaultAccountManager) SaveSetupKey(ctx context.Context, accountID str
|
||||
|
||||
// ListSetupKeys returns a list of all setup keys of the account
|
||||
func (am *DefaultAccountManager) ListSetupKeys(ctx context.Context, accountID, userID string) ([]*types.SetupKey, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.SetupKeys, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
return am.Store.GetAccountSetupKeys(ctx, store.LockingStrengthShare, accountID)
|
||||
@@ -193,17 +180,12 @@ func (am *DefaultAccountManager) ListSetupKeys(ctx context.Context, accountID, u
|
||||
|
||||
// GetSetupKey looks up a SetupKey by KeyID, returns NotFound error if not found.
|
||||
func (am *DefaultAccountManager) GetSetupKey(ctx context.Context, accountID, userID, keyID string) (*types.SetupKey, error) {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.SetupKeys, operations.Read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return nil, status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return nil, status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
setupKey, err := am.Store.GetSetupKeyByID(ctx, store.LockingStrengthShare, accountID, keyID)
|
||||
@@ -221,17 +203,12 @@ func (am *DefaultAccountManager) GetSetupKey(ctx context.Context, accountID, use
|
||||
|
||||
// DeleteSetupKey removes the setup key from the account
|
||||
func (am *DefaultAccountManager) DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error {
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.SetupKeys, operations.Delete)
|
||||
if err != nil {
|
||||
return err
|
||||
return status.NewPermissionValidationError(err)
|
||||
}
|
||||
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.IsRegularUser() {
|
||||
return status.NewAdminPermissionError()
|
||||
if !allowed {
|
||||
return status.NewPermissionDeniedError()
|
||||
}
|
||||
|
||||
var deletedSetupKey *types.SetupKey
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestDefaultAccountManager_SaveSetupKey(t *testing.T) {
|
||||
Name: "group_name_2",
|
||||
Peers: []string{},
|
||||
},
|
||||
})
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func TestDefaultAccountManager_CreateSetupKey(t *testing.T) {
|
||||
ID: "group_1",
|
||||
Name: "group_name_1",
|
||||
Peers: []string{},
|
||||
})
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func TestDefaultAccountManager_CreateSetupKey(t *testing.T) {
|
||||
ID: "group_2",
|
||||
Name: "group_name_2",
|
||||
Peers: []string{},
|
||||
})
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -403,7 +403,7 @@ func TestSetupKeyAccountPeersUpdate(t *testing.T) {
|
||||
ID: "groupA",
|
||||
Name: "GroupA",
|
||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||
})
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
policy := &types.Policy{
|
||||
@@ -418,7 +418,7 @@ func TestSetupKeyAccountPeersUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, userID, policy)
|
||||
_, err = manager.SavePolicy(context.Background(), account.Id, userID, policy, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user