Merge branch 'main' into feature/port-forwarding

This commit is contained in:
Viktor Liu 2025-02-05 10:15:37 +01:00
commit b2a5b29fb2
44 changed files with 4394 additions and 30 deletions

View File

@ -2,6 +2,8 @@ package auth
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
@ -11,7 +13,10 @@ import (
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/util/embeddedroots"
)
// HostedGrantType grant type for device flow on Hosted
@ -56,6 +61,18 @@ func NewDeviceAuthorizationFlow(config internal.DeviceAuthProviderConfig) (*Devi
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
httpTransport.MaxIdleConns = 5
certPool, err := x509.SystemCertPool()
if err != nil || certPool == nil {
log.Debugf("System cert pool not available; falling back to embedded cert, error: %v", err)
certPool = embeddedroots.Get()
} else {
log.Debug("Using system certificate pool.")
}
httpTransport.TLSClientConfig = &tls.Config{
RootCAs: certPool,
}
httpClient := &http.Client{
Timeout: 10 * time.Second,
Transport: httpTransport,

View File

@ -1,8 +1,9 @@
package peer
import (
"github.com/magiconair/properties/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestConnStatus_String(t *testing.T) {

View File

@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/internal/peer/guard"

2
go.mod
View File

@ -54,7 +54,6 @@ require (
github.com/hashicorp/go-version v1.6.0
github.com/libdns/route53 v1.5.0
github.com/libp2p/go-netroute v0.2.1
github.com/magiconair/properties v1.8.7
github.com/mattn/go-sqlite3 v1.14.22
github.com/mdlayher/socket v0.5.1
github.com/miekg/dns v1.1.59
@ -185,6 +184,7 @@ require (
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mholt/acmez/v2 v2.0.1 // indirect

View File

@ -0,0 +1,54 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// AccountsAPI APIs for accounts, do not use directly
type AccountsAPI struct {
c *Client
}
// List list all accounts, only returns one account always
// See more: https://docs.netbird.io/api/resources/accounts#list-all-accounts
func (a *AccountsAPI) List(ctx context.Context) ([]api.Account, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/accounts", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Account](resp)
return ret, err
}
// Update update account settings
// See more: https://docs.netbird.io/api/resources/accounts#update-an-account
func (a *AccountsAPI) Update(ctx context.Context, accountID string, request api.PutApiAccountsAccountIdJSONRequestBody) (*api.Account, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/accounts/"+accountID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Account](resp)
return &ret, err
}
// Delete delete account
// See more: https://docs.netbird.io/api/resources/accounts#delete-an-account
func (a *AccountsAPI) Delete(ctx context.Context, accountID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/accounts/"+accountID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,169 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testAccount = api.Account{
Id: "Test",
Settings: api.AccountSettings{
Extra: &api.AccountExtraSettings{
PeerApprovalEnabled: ptr(false),
},
GroupsPropagationEnabled: ptr(true),
JwtGroupsEnabled: ptr(false),
PeerInactivityExpiration: 7,
PeerInactivityExpirationEnabled: true,
PeerLoginExpiration: 24,
PeerLoginExpirationEnabled: true,
RegularUsersViewBlocked: false,
RoutingPeerDnsResolutionEnabled: ptr(false),
},
}
)
func TestAccounts_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/accounts", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Account{testAccount})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Accounts.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testAccount, ret[0])
})
}
func TestAccounts_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/accounts", 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.Accounts.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestAccounts_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/accounts/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiAccountsAccountIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, *req.Settings.RoutingPeerDnsResolutionEnabled)
retBytes, _ := json.Marshal(testAccount)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Accounts.Update(context.Background(), "Test", api.PutApiAccountsAccountIdJSONRequestBody{
Settings: api.AccountSettings{
RoutingPeerDnsResolutionEnabled: ptr(true),
},
})
require.NoError(t, err)
assert.Equal(t, testAccount, *ret)
})
}
func TestAccounts_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/accounts/Test", 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.Accounts.Update(context.Background(), "Test", api.PutApiAccountsAccountIdJSONRequestBody{
Settings: api.AccountSettings{
RoutingPeerDnsResolutionEnabled: ptr(true),
},
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestAccounts_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/accounts/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Accounts.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestAccounts_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/accounts/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Accounts.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestAccounts_Integration_List(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
accounts, err := c.Accounts.List(context.Background())
require.NoError(t, err)
assert.Len(t, accounts, 1)
assert.Equal(t, "bf1c8084-ba50-4ce7-9439-34653001fc3b", accounts[0].Id)
assert.Equal(t, false, *accounts[0].Settings.Extra.PeerApprovalEnabled)
})
}
func TestAccounts_Integration_Update(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
accounts, err := c.Accounts.List(context.Background())
require.NoError(t, err)
assert.Len(t, accounts, 1)
accounts[0].Settings.JwtAllowGroups = ptr([]string{"test"})
account, err := c.Accounts.Update(context.Background(), accounts[0].Id, api.AccountRequest{
Settings: accounts[0].Settings,
})
require.NoError(t, err)
assert.Equal(t, accounts[0].Id, account.Id)
assert.Equal(t, []string{"test"}, *account.Settings.JwtAllowGroups)
})
}
// Account deletion on MySQL and PostgreSQL databases causes unknown errors
// func TestAccounts_Integration_Delete(t *testing.T) {
// withBlackBoxServer(t, func(c *Client) {
// accounts, err := c.Accounts.List(context.Background())
// require.NoError(t, err)
// assert.Len(t, accounts, 1)
// err = c.Accounts.Delete(context.Background(), accounts[0].Id)
// require.NoError(t, err)
// _, err = c.Accounts.List(context.Background())
// assert.Error(t, err)
// })
// }

View File

@ -0,0 +1,133 @@
package rest
import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"github.com/netbirdio/netbird/management/server/http/util"
)
// Client Management service HTTP REST API Client
type Client struct {
managementURL string
authHeader string
// Accounts NetBird account APIs
// see more: https://docs.netbird.io/api/resources/accounts
Accounts *AccountsAPI
// Users NetBird users APIs
// see more: https://docs.netbird.io/api/resources/users
Users *UsersAPI
// Tokens NetBird tokens APIs
// see more: https://docs.netbird.io/api/resources/tokens
Tokens *TokensAPI
// Peers NetBird peers APIs
// see more: https://docs.netbird.io/api/resources/peers
Peers *PeersAPI
// SetupKeys NetBird setup keys APIs
// see more: https://docs.netbird.io/api/resources/setup-keys
SetupKeys *SetupKeysAPI
// Groups NetBird groups APIs
// see more: https://docs.netbird.io/api/resources/groups
Groups *GroupsAPI
// Policies NetBird policies APIs
// see more: https://docs.netbird.io/api/resources/policies
Policies *PoliciesAPI
// PostureChecks NetBird posture checks APIs
// see more: https://docs.netbird.io/api/resources/posture-checks
PostureChecks *PostureChecksAPI
// Networks NetBird networks APIs
// see more: https://docs.netbird.io/api/resources/networks
Networks *NetworksAPI
// Routes NetBird routes APIs
// see more: https://docs.netbird.io/api/resources/routes
Routes *RoutesAPI
// DNS NetBird DNS APIs
// see more: https://docs.netbird.io/api/resources/routes
DNS *DNSAPI
// GeoLocation NetBird Geo Location APIs
// see more: https://docs.netbird.io/api/resources/geo-locations
GeoLocation *GeoLocationAPI
// Events NetBird Events APIs
// see more: https://docs.netbird.io/api/resources/events
Events *EventsAPI
}
// New initialize new Client instance
func New(managementURL, token string) *Client {
client := &Client{
managementURL: managementURL,
authHeader: "Token " + token,
}
client.Accounts = &AccountsAPI{client}
client.Users = &UsersAPI{client}
client.Tokens = &TokensAPI{client}
client.Peers = &PeersAPI{client}
client.SetupKeys = &SetupKeysAPI{client}
client.Groups = &GroupsAPI{client}
client.Policies = &PoliciesAPI{client}
client.PostureChecks = &PostureChecksAPI{client}
client.Networks = &NetworksAPI{client}
client.Routes = &RoutesAPI{client}
client.DNS = &DNSAPI{client}
client.GeoLocation = &GeoLocationAPI{client}
client.Events = &EventsAPI{client}
return client
}
func (c *Client) newRequest(ctx context.Context, method, path string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, method, c.managementURL+path, body)
if err != nil {
return nil, err
}
req.Header.Add("Authorization", c.authHeader)
req.Header.Add("Accept", "application/json")
if body != nil {
req.Header.Add("Content-Type", "application/json")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode > 299 {
parsedErr, pErr := parseResponse[util.ErrorResponse](resp)
if pErr != nil {
return nil, err
}
return nil, errors.New(parsedErr.Message)
}
return resp, nil
}
func parseResponse[T any](resp *http.Response) (T, error) {
var ret T
if resp.Body == nil {
return ret, errors.New("No body")
}
bs, err := io.ReadAll(resp.Body)
if err != nil {
return ret, err
}
err = json.Unmarshal(bs, &ret)
return ret, err
}

View File

@ -0,0 +1,30 @@
package rest
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools"
)
func withMockClient(callback func(*Client, *http.ServeMux)) {
mux := &http.ServeMux{}
server := httptest.NewServer(mux)
defer server.Close()
c := New(server.URL, "ABC")
callback(c, mux)
}
func ptr[T any, PT *T](x T) PT {
return &x
}
func withBlackBoxServer(t *testing.T, callback func(*Client)) {
t.Helper()
handler, _, _ := testing_tools.BuildApiBlackBoxWithDBState(t, "../../server/testdata/store.sql", nil, false)
server := httptest.NewServer(handler)
defer server.Close()
c := New(server.URL, "nbp_apTmlmUXHSC4PKmHwtIZNaGr8eqcVI2gMURp")
callback(c)
}

View File

@ -0,0 +1,110 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// DNSAPI APIs for DNS Management, do not use directly
type DNSAPI struct {
c *Client
}
// ListNameserverGroups list all nameserver groups
// See more: https://docs.netbird.io/api/resources/dns#list-all-nameserver-groups
func (a *DNSAPI) ListNameserverGroups(ctx context.Context) ([]api.NameserverGroup, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/dns/nameservers", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.NameserverGroup](resp)
return ret, err
}
// GetNameserverGroup get nameserver group info
// See more: https://docs.netbird.io/api/resources/dns#retrieve-a-nameserver-group
func (a *DNSAPI) GetNameserverGroup(ctx context.Context, nameserverGroupID string) (*api.NameserverGroup, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/dns/nameservers/"+nameserverGroupID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NameserverGroup](resp)
return &ret, err
}
// CreateNameserverGroup create new nameserver group
// See more: https://docs.netbird.io/api/resources/dns#create-a-nameserver-group
func (a *DNSAPI) CreateNameserverGroup(ctx context.Context, request api.PostApiDnsNameserversJSONRequestBody) (*api.NameserverGroup, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/dns/nameservers", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NameserverGroup](resp)
return &ret, err
}
// UpdateNameserverGroup update nameserver group info
// See more: https://docs.netbird.io/api/resources/dns#update-a-nameserver-group
func (a *DNSAPI) UpdateNameserverGroup(ctx context.Context, nameserverGroupID string, request api.PutApiDnsNameserversNsgroupIdJSONRequestBody) (*api.NameserverGroup, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/dns/nameservers/"+nameserverGroupID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NameserverGroup](resp)
return &ret, err
}
// DeleteNameserverGroup delete nameserver group
// See more: https://docs.netbird.io/api/resources/dns#delete-a-nameserver-group
func (a *DNSAPI) DeleteNameserverGroup(ctx context.Context, nameserverGroupID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/dns/nameservers/"+nameserverGroupID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
// GetSettings get DNS settings
// See more: https://docs.netbird.io/api/resources/dns#retrieve-dns-settings
func (a *DNSAPI) GetSettings(ctx context.Context) (*api.DNSSettings, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/dns/settings", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.DNSSettings](resp)
return &ret, err
}
// UpdateSettings update DNS settings
// See more: https://docs.netbird.io/api/resources/dns#update-dns-settings
func (a *DNSAPI) UpdateSettings(ctx context.Context, request api.PutApiDnsSettingsJSONRequestBody) (*api.DNSSettings, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/dns/settings", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.DNSSettings](resp)
return &ret, err
}

View File

@ -0,0 +1,295 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testNameserverGroup = api.NameserverGroup{
Id: "Test",
Name: "wow",
}
testSettings = api.DNSSettings{
DisabledManagementGroups: []string{"gone"},
}
)
func TestDNSNameserverGroup_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.NameserverGroup{testNameserverGroup})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNS.ListNameserverGroups(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testNameserverGroup, ret[0])
})
}
func TestDNSNameserverGroup_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers", 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.DNS.ListNameserverGroups(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestDNSNameserverGroup_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testNameserverGroup)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNS.GetNameserverGroup(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testNameserverGroup, *ret)
})
}
func TestDNSNameserverGroup_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers/Test", 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.DNS.GetNameserverGroup(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestDNSNameserverGroup_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiDnsNameserversJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testNameserverGroup)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNS.CreateNameserverGroup(context.Background(), api.PostApiDnsNameserversJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testNameserverGroup, *ret)
})
}
func TestDNSNameserverGroup_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers", 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.DNS.CreateNameserverGroup(context.Background(), api.PostApiDnsNameserversJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestDNSNameserverGroup_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiDnsNameserversNsgroupIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testNameserverGroup)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNS.UpdateNameserverGroup(context.Background(), "Test", api.PutApiDnsNameserversNsgroupIdJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testNameserverGroup, *ret)
})
}
func TestDNSNameserverGroup_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers/Test", 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.DNS.UpdateNameserverGroup(context.Background(), "Test", api.PutApiDnsNameserversNsgroupIdJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestDNSNameserverGroup_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.DNS.DeleteNameserverGroup(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestDNSNameserverGroup_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/nameservers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.DNS.DeleteNameserverGroup(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestDNSSettings_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/settings", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testSettings)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNS.GetSettings(context.Background())
require.NoError(t, err)
assert.Equal(t, testSettings, *ret)
})
}
func TestDNSSettings_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/settings", 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.DNS.GetSettings(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestDNSSettings_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/settings", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiDnsSettingsJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, []string{"test"}, req.DisabledManagementGroups)
retBytes, _ := json.Marshal(testSettings)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNS.UpdateSettings(context.Background(), api.PutApiDnsSettingsJSONRequestBody{
DisabledManagementGroups: []string{"test"},
})
require.NoError(t, err)
assert.Equal(t, testSettings, *ret)
})
}
func TestDNSSettings_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/settings", 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.DNS.UpdateSettings(context.Background(), api.PutApiDnsSettingsJSONRequestBody{
DisabledManagementGroups: []string{"test"},
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestDNS_Integration(t *testing.T) {
nsGroupReq := api.NameserverGroupRequest{
Description: "Test",
Enabled: true,
Groups: []string{"cs1tnh0hhcjnqoiuebeg"},
Name: "test",
Nameservers: []api.Nameserver{
{
Ip: "8.8.8.8",
NsType: api.NameserverNsTypeUdp,
Port: 53,
},
},
Primary: true,
SearchDomainsEnabled: false,
}
withBlackBoxServer(t, func(c *Client) {
// Create
nsGroup, err := c.DNS.CreateNameserverGroup(context.Background(), nsGroupReq)
require.NoError(t, err)
// List
nsGroups, err := c.DNS.ListNameserverGroups(context.Background())
require.NoError(t, err)
assert.Equal(t, *nsGroup, nsGroups[0])
// Update
nsGroupReq.Description = "TestUpdate"
nsGroup, err = c.DNS.UpdateNameserverGroup(context.Background(), nsGroup.Id, nsGroupReq)
require.NoError(t, err)
assert.Equal(t, "TestUpdate", nsGroup.Description)
// Delete
err = c.DNS.DeleteNameserverGroup(context.Background(), nsGroup.Id)
require.NoError(t, err)
// List again to ensure deletion
nsGroups, err = c.DNS.ListNameserverGroups(context.Background())
require.NoError(t, err)
assert.Len(t, nsGroups, 0)
})
}

View File

@ -0,0 +1,24 @@
package rest
import (
"context"
"github.com/netbirdio/netbird/management/server/http/api"
)
// EventsAPI APIs for Events, do not use directly
type EventsAPI struct {
c *Client
}
// List list all events
// See more: https://docs.netbird.io/api/resources/events#list-all-events
func (a *EventsAPI) List(ctx context.Context) ([]api.Event, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/events", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Event](resp)
return ret, err
}

View File

@ -0,0 +1,65 @@
package rest
import (
"context"
"encoding/json"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testEvent = api.Event{
Activity: "AccountCreate",
ActivityCode: api.EventActivityCodeAccountCreate,
}
)
func TestEvents_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/events", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Event{testEvent})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Events.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testEvent, ret[0])
})
}
func TestEvents_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/events", 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.Events.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestEvents_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
// Do something that would trigger any event
_, err := c.SetupKeys.Create(context.Background(), api.CreateSetupKeyRequest{
Ephemeral: ptr(true),
Name: "TestSetupKey",
Type: "reusable",
})
require.NoError(t, err)
events, err := c.Events.List(context.Background())
require.NoError(t, err)
assert.NotEmpty(t, events)
})
}

View File

@ -0,0 +1,36 @@
package rest
import (
"context"
"github.com/netbirdio/netbird/management/server/http/api"
)
// GeoLocationAPI APIs for Geo-Location, do not use directly
type GeoLocationAPI struct {
c *Client
}
// ListCountries list all country codes
// See more: https://docs.netbird.io/api/resources/geo-locations#list-all-country-codes
func (a *GeoLocationAPI) ListCountries(ctx context.Context) ([]api.Country, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/locations/countries", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Country](resp)
return ret, err
}
// ListCountryCities Get a list of all English city names for a given country code
// See more: https://docs.netbird.io/api/resources/geo-locations#list-all-city-names-by-country
func (a *GeoLocationAPI) ListCountryCities(ctx context.Context, countryCode string) ([]api.City, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/locations/countries/"+countryCode+"/cities", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.City](resp)
return ret, err
}

View File

@ -0,0 +1,96 @@
package rest
import (
"context"
"encoding/json"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testCountry = api.Country{
CountryCode: "DE",
CountryName: "Germany",
}
testCity = api.City{
CityName: "Berlin",
GeonameId: 2950158,
}
)
func TestGeo_ListCountries_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/locations/countries", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Country{testCountry})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GeoLocation.ListCountries(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testCountry, ret[0])
})
}
func TestGeo_ListCountries_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/locations/countries", 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.GeoLocation.ListCountries(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestGeo_ListCountryCities_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/locations/countries/Test/cities", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.City{testCity})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GeoLocation.ListCountryCities(context.Background(), "Test")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testCity, ret[0])
})
}
func TestGeo_ListCountryCities_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/locations/countries/Test/cities", 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.GeoLocation.ListCountryCities(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestGeo_Integration(t *testing.T) {
// Blackbox is initialized with empty GeoLocations
withBlackBoxServer(t, func(c *Client) {
countries, err := c.GeoLocation.ListCountries(context.Background())
require.NoError(t, err)
assert.Empty(t, countries)
cities, err := c.GeoLocation.ListCountryCities(context.Background(), "DE")
require.NoError(t, err)
assert.Empty(t, cities)
})
}

View File

@ -0,0 +1,82 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// GroupsAPI APIs for Groups, do not use directly
type GroupsAPI struct {
c *Client
}
// List list all groups
// See more: https://docs.netbird.io/api/resources/groups#list-all-groups
func (a *GroupsAPI) List(ctx context.Context) ([]api.Group, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/groups", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Group](resp)
return ret, err
}
// Get get group info
// See more: https://docs.netbird.io/api/resources/groups#retrieve-a-group
func (a *GroupsAPI) Get(ctx context.Context, groupID string) (*api.Group, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/groups/"+groupID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Group](resp)
return &ret, err
}
// Create create new group
// See more: https://docs.netbird.io/api/resources/groups#create-a-group
func (a *GroupsAPI) Create(ctx context.Context, request api.PostApiGroupsJSONRequestBody) (*api.Group, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/groups", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Group](resp)
return &ret, err
}
// Update update group info
// See more: https://docs.netbird.io/api/resources/groups#update-a-group
func (a *GroupsAPI) Update(ctx context.Context, groupID string, request api.PutApiGroupsGroupIdJSONRequestBody) (*api.Group, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/groups/"+groupID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Group](resp)
return &ret, err
}
// Delete delete group
// See more: https://docs.netbird.io/api/resources/groups#delete-a-group
func (a *GroupsAPI) Delete(ctx context.Context, groupID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/groups/"+groupID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,210 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testGroup = api.Group{
Id: "Test",
Name: "wow",
PeersCount: 0,
}
)
func TestGroups_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Group{testGroup})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Groups.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testGroup, ret[0])
})
}
func TestGroups_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups", 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.Groups.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestGroups_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testGroup)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Groups.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testGroup, *ret)
})
}
func TestGroups_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups/Test", 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.Groups.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestGroups_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiGroupsJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testGroup)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Groups.Create(context.Background(), api.PostApiGroupsJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testGroup, *ret)
})
}
func TestGroups_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups", 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.Groups.Create(context.Background(), api.PostApiGroupsJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestGroups_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiGroupsGroupIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testGroup)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Groups.Update(context.Background(), "Test", api.PutApiGroupsGroupIdJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testGroup, *ret)
})
}
func TestGroups_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups/Test", 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.Groups.Update(context.Background(), "Test", api.PutApiGroupsGroupIdJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestGroups_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Groups.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestGroups_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/groups/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Groups.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestGroups_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
groups, err := c.Groups.List(context.Background())
require.NoError(t, err)
assert.Len(t, groups, 1)
group, err := c.Groups.Create(context.Background(), api.GroupRequest{
Name: "Test",
})
require.NoError(t, err)
assert.Equal(t, "Test", group.Name)
assert.NotEmpty(t, group.Id)
group, err = c.Groups.Update(context.Background(), group.Id, api.GroupRequest{
Name: "Testnt",
})
require.NoError(t, err)
assert.Equal(t, "Testnt", group.Name)
err = c.Groups.Delete(context.Background(), group.Id)
require.NoError(t, err)
groups, err = c.Groups.List(context.Background())
require.NoError(t, err)
assert.Len(t, groups, 1)
})
}

View File

@ -0,0 +1,246 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// NetworksAPI APIs for Networks, do not use directly
type NetworksAPI struct {
c *Client
}
// List list all networks
// See more: https://docs.netbird.io/api/resources/networks#list-all-networks
func (a *NetworksAPI) List(ctx context.Context) ([]api.Network, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/networks", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Network](resp)
return ret, err
}
// Get get network info
// See more: https://docs.netbird.io/api/resources/networks#retrieve-a-network
func (a *NetworksAPI) Get(ctx context.Context, networkID string) (*api.Network, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/networks/"+networkID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Network](resp)
return &ret, err
}
// Create create new network
// See more: https://docs.netbird.io/api/resources/networks#create-a-network
func (a *NetworksAPI) Create(ctx context.Context, request api.PostApiNetworksJSONRequestBody) (*api.Network, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/networks", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Network](resp)
return &ret, err
}
// Update update network
// See more: https://docs.netbird.io/api/resources/networks#update-a-network
func (a *NetworksAPI) Update(ctx context.Context, networkID string, request api.PutApiNetworksNetworkIdJSONRequestBody) (*api.Network, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/networks/"+networkID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Network](resp)
return &ret, err
}
// Delete delete network
// See more: https://docs.netbird.io/api/resources/networks#delete-a-network
func (a *NetworksAPI) Delete(ctx context.Context, networkID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/networks/"+networkID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
// NetworkResourcesAPI APIs for Network Resources, do not use directly
type NetworkResourcesAPI struct {
c *Client
networkID string
}
// Resources APIs for network resources
func (a *NetworksAPI) Resources(networkID string) *NetworkResourcesAPI {
return &NetworkResourcesAPI{
c: a.c,
networkID: networkID,
}
}
// List list all resources in networks
// See more: https://docs.netbird.io/api/resources/networks#list-all-network-resources
func (a *NetworkResourcesAPI) List(ctx context.Context) ([]api.NetworkResource, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/networks/"+a.networkID+"/resources", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.NetworkResource](resp)
return ret, err
}
// Get get network resource info
// See more: https://docs.netbird.io/api/resources/networks#retrieve-a-network-resource
func (a *NetworkResourcesAPI) Get(ctx context.Context, networkResourceID string) (*api.NetworkResource, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/networks/"+a.networkID+"/resources/"+networkResourceID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NetworkResource](resp)
return &ret, err
}
// Create create new network resource
// See more: https://docs.netbird.io/api/resources/networks#create-a-network-resource
func (a *NetworkResourcesAPI) Create(ctx context.Context, request api.PostApiNetworksNetworkIdResourcesJSONRequestBody) (*api.NetworkResource, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/networks/"+a.networkID+"/resources", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NetworkResource](resp)
return &ret, err
}
// Update update network resource
// See more: https://docs.netbird.io/api/resources/networks#update-a-network-resource
func (a *NetworkResourcesAPI) Update(ctx context.Context, networkResourceID string, request api.PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody) (*api.NetworkResource, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/networks/"+a.networkID+"/resources/"+networkResourceID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NetworkResource](resp)
return &ret, err
}
// Delete delete network resource
// See more: https://docs.netbird.io/api/resources/networks#delete-a-network-resource
func (a *NetworkResourcesAPI) Delete(ctx context.Context, networkResourceID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/networks/"+a.networkID+"/resources/"+networkResourceID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
// NetworkRoutersAPI APIs for Network Routers, do not use directly
type NetworkRoutersAPI struct {
c *Client
networkID string
}
// Routers APIs for network routers
func (a *NetworksAPI) Routers(networkID string) *NetworkRoutersAPI {
return &NetworkRoutersAPI{
c: a.c,
networkID: networkID,
}
}
// List list all routers in networks
// See more: https://docs.netbird.io/api/routers/networks#list-all-network-routers
func (a *NetworkRoutersAPI) List(ctx context.Context) ([]api.NetworkRouter, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/networks/"+a.networkID+"/routers", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.NetworkRouter](resp)
return ret, err
}
// Get get network router info
// See more: https://docs.netbird.io/api/routers/networks#retrieve-a-network-router
func (a *NetworkRoutersAPI) Get(ctx context.Context, networkRouterID string) (*api.NetworkRouter, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/networks/"+a.networkID+"/routers/"+networkRouterID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NetworkRouter](resp)
return &ret, err
}
// Create create new network router
// See more: https://docs.netbird.io/api/routers/networks#create-a-network-router
func (a *NetworkRoutersAPI) Create(ctx context.Context, request api.PostApiNetworksNetworkIdRoutersJSONRequestBody) (*api.NetworkRouter, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/networks/"+a.networkID+"/routers", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NetworkRouter](resp)
return &ret, err
}
// Update update network router
// See more: https://docs.netbird.io/api/routers/networks#update-a-network-router
func (a *NetworkRoutersAPI) Update(ctx context.Context, networkRouterID string, request api.PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody) (*api.NetworkRouter, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/networks/"+a.networkID+"/routers/"+networkRouterID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.NetworkRouter](resp)
return &ret, err
}
// Delete delete network router
// See more: https://docs.netbird.io/api/routers/networks#delete-a-network-router
func (a *NetworkRoutersAPI) Delete(ctx context.Context, networkRouterID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/networks/"+a.networkID+"/routers/"+networkRouterID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,589 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testNetwork = api.Network{
Id: "Test",
Name: "wow",
}
testNetworkResource = api.NetworkResource{
Description: ptr("meaw"),
Id: "awa",
}
testNetworkRouter = api.NetworkRouter{
Id: "ouch",
}
)
func TestNetworks_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Network{testNetwork})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testNetwork, ret[0])
})
}
func TestNetworks_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks", 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.Networks.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestNetworks_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testNetwork)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testNetwork, *ret)
})
}
func TestNetworks_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Test", 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.Networks.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestNetworks_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiNetworksJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testNetwork)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Create(context.Background(), api.PostApiNetworksJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testNetwork, *ret)
})
}
func TestNetworks_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks", 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.Networks.Create(context.Background(), api.PostApiNetworksJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestNetworks_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiNetworksNetworkIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testNetwork)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Update(context.Background(), "Test", api.PutApiNetworksNetworkIdJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testNetwork, *ret)
})
}
func TestNetworks_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Test", 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.Networks.Update(context.Background(), "Test", api.PutApiNetworksNetworkIdJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestNetworks_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Networks.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestNetworks_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Networks.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestNetworks_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
network, err := c.Networks.Create(context.Background(), api.NetworkRequest{
Description: ptr("TestNetwork"),
Name: "Test",
})
assert.NoError(t, err)
assert.Equal(t, "Test", network.Name)
networks, err := c.Networks.List(context.Background())
assert.NoError(t, err)
assert.Empty(t, networks)
network, err = c.Networks.Update(context.Background(), "TestID", api.NetworkRequest{
Description: ptr("TestNetwork?"),
Name: "Test",
})
assert.NoError(t, err)
assert.Equal(t, "TestNetwork?", *network.Description)
err = c.Networks.Delete(context.Background(), "TestID")
assert.NoError(t, err)
})
}
func TestNetworkResources_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.NetworkResource{testNetworkResource})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Resources("Meow").List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testNetworkResource, ret[0])
})
}
func TestNetworkResources_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources", 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.Networks.Resources("Meow").List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestNetworkResources_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testNetworkResource)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Resources("Meow").Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testNetworkResource, *ret)
})
}
func TestNetworkResources_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources/Test", 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.Networks.Resources("Meow").Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestNetworkResources_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiNetworksNetworkIdResourcesJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testNetworkResource)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Resources("Meow").Create(context.Background(), api.PostApiNetworksNetworkIdResourcesJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testNetworkResource, *ret)
})
}
func TestNetworkResources_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources", 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.Networks.Resources("Meow").Create(context.Background(), api.PostApiNetworksNetworkIdResourcesJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestNetworkResources_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testNetworkResource)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Resources("Meow").Update(context.Background(), "Test", api.PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testNetworkResource, *ret)
})
}
func TestNetworkResources_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources/Test", 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.Networks.Resources("Meow").Update(context.Background(), "Test", api.PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestNetworkResources_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Networks.Resources("Meow").Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestNetworkResources_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/resources/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Networks.Resources("Meow").Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestNetworkResources_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
_, err := c.Networks.Resources("TestNetwork").Create(context.Background(), api.NetworkResourceRequest{
Address: "test.com",
Description: ptr("Description"),
Enabled: false,
Groups: []string{"test"},
Name: "test",
})
assert.NoError(t, err)
_, err = c.Networks.Resources("TestNetwork").List(context.Background())
assert.NoError(t, err)
_, err = c.Networks.Resources("TestNetwork").Get(context.Background(), "TestResource")
assert.NoError(t, err)
_, err = c.Networks.Resources("TestNetwork").Update(context.Background(), "TestResource", api.NetworkResourceRequest{
Address: "testnt.com",
})
assert.NoError(t, err)
err = c.Networks.Resources("TestNetwork").Delete(context.Background(), "TestResource")
assert.NoError(t, err)
})
}
func TestNetworkRouters_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.NetworkRouter{testNetworkRouter})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Routers("Meow").List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testNetworkRouter, ret[0])
})
}
func TestNetworkRouters_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers", 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.Networks.Routers("Meow").List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestNetworkRouters_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testNetworkRouter)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Routers("Meow").Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testNetworkRouter, *ret)
})
}
func TestNetworkRouters_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers/Test", 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.Networks.Routers("Meow").Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestNetworkRouters_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiNetworksNetworkIdRoutersJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "test", *req.Peer)
retBytes, _ := json.Marshal(testNetworkRouter)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Routers("Meow").Create(context.Background(), api.PostApiNetworksNetworkIdRoutersJSONRequestBody{
Peer: ptr("test"),
})
require.NoError(t, err)
assert.Equal(t, testNetworkRouter, *ret)
})
}
func TestNetworkRouters_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers", 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.Networks.Routers("Meow").Create(context.Background(), api.PostApiNetworksNetworkIdRoutersJSONRequestBody{
Peer: ptr("test"),
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestNetworkRouters_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "test", *req.Peer)
retBytes, _ := json.Marshal(testNetworkRouter)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Networks.Routers("Meow").Update(context.Background(), "Test", api.PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody{
Peer: ptr("test"),
})
require.NoError(t, err)
assert.Equal(t, testNetworkRouter, *ret)
})
}
func TestNetworkRouters_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers/Test", 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.Networks.Routers("Meow").Update(context.Background(), "Test", api.PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody{
Peer: ptr("test"),
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestNetworkRouters_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Networks.Routers("Meow").Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestNetworkRouters_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/networks/Meow/routers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Networks.Routers("Meow").Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestNetworkRouters_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
_, err := c.Networks.Routers("TestNetwork").Create(context.Background(), api.NetworkRouterRequest{
Enabled: false,
Masquerade: false,
Metric: 9999,
PeerGroups: ptr([]string{"test"}),
})
assert.NoError(t, err)
_, err = c.Networks.Routers("TestNetwork").List(context.Background())
assert.NoError(t, err)
_, err = c.Networks.Routers("TestNetwork").Get(context.Background(), "TestRouter")
assert.NoError(t, err)
_, err = c.Networks.Routers("TestNetwork").Update(context.Background(), "TestRouter", api.NetworkRouterRequest{
Enabled: true,
})
assert.NoError(t, err)
err = c.Networks.Routers("TestNetwork").Delete(context.Background(), "TestRouter")
assert.NoError(t, err)
})
}

View File

@ -0,0 +1,78 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// PeersAPI APIs for peers, do not use directly
type PeersAPI struct {
c *Client
}
// List list all peers
// See more: https://docs.netbird.io/api/resources/peers#list-all-peers
func (a *PeersAPI) List(ctx context.Context) ([]api.Peer, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/peers", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Peer](resp)
return ret, err
}
// Get retrieve a peer
// See more: https://docs.netbird.io/api/resources/peers#retrieve-a-peer
func (a *PeersAPI) Get(ctx context.Context, peerID string) (*api.Peer, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/peers/"+peerID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Peer](resp)
return &ret, err
}
// Update update information for a peer
// See more: https://docs.netbird.io/api/resources/peers#update-a-peer
func (a *PeersAPI) Update(ctx context.Context, peerID string, request api.PutApiPeersPeerIdJSONRequestBody) (*api.Peer, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/peers/"+peerID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Peer](resp)
return &ret, err
}
// Delete delete a peer
// See more: https://docs.netbird.io/api/resources/peers#delete-a-peer
func (a *PeersAPI) Delete(ctx context.Context, peerID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/peers/"+peerID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
// ListAccessiblePeers list all peers that the specified peer can connect to within the network
// See more: https://docs.netbird.io/api/resources/peers#list-accessible-peers
func (a *PeersAPI) ListAccessiblePeers(ctx context.Context, peerID string) ([]api.Peer, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/peers/"+peerID+"/accessible-peers", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Peer](resp)
return ret, err
}

View File

@ -0,0 +1,203 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testPeer = api.Peer{
ApprovalRequired: false,
Connected: false,
ConnectionIp: "127.0.0.1",
DnsLabel: "test",
Id: "Test",
}
)
func TestPeers_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Peer{testPeer})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testPeer, ret[0])
})
}
func TestPeers_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers", 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.Peers.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeers_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testPeer)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testPeer, *ret)
})
}
func TestPeers_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test", 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.Peers.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeers_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiPeersPeerIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, req.InactivityExpirationEnabled)
retBytes, _ := json.Marshal(testPeer)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.Update(context.Background(), "Test", api.PutApiPeersPeerIdJSONRequestBody{
InactivityExpirationEnabled: true,
})
require.NoError(t, err)
assert.Equal(t, testPeer, *ret)
})
}
func TestPeers_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test", 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.Peers.Update(context.Background(), "Test", api.PutApiPeersPeerIdJSONRequestBody{
InactivityExpirationEnabled: false,
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPeers_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Peers.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestPeers_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Peers.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestPeers_ListAccessiblePeers_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/accessible-peers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Peer{testPeer})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.ListAccessiblePeers(context.Background(), "Test")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testPeer, ret[0])
})
}
func TestPeers_ListAccessiblePeers_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/accessible-peers", 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.Peers.ListAccessiblePeers(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeers_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
peers, err := c.Peers.List(context.Background())
require.NoError(t, err)
require.NotEmpty(t, peers)
peer, err := c.Peers.Get(context.Background(), peers[0].Id)
require.NoError(t, err)
assert.Equal(t, peers[0].Id, peer.Id)
peer, err = c.Peers.Update(context.Background(), peer.Id, api.PeerRequest{
LoginExpirationEnabled: true,
Name: "Test",
SshEnabled: false,
ApprovalRequired: ptr(false),
InactivityExpirationEnabled: false,
})
require.NoError(t, err)
assert.Equal(t, true, peer.LoginExpirationEnabled)
accessiblePeers, err := c.Peers.ListAccessiblePeers(context.Background(), peer.Id)
require.NoError(t, err)
assert.Empty(t, accessiblePeers)
err = c.Peers.Delete(context.Background(), peer.Id)
require.NoError(t, err)
})
}

View File

@ -0,0 +1,82 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// PoliciesAPI APIs for Policies, do not use directly
type PoliciesAPI struct {
c *Client
}
// List list all policies
// See more: https://docs.netbird.io/api/resources/policies#list-all-policies
func (a *PoliciesAPI) List(ctx context.Context) ([]api.Policy, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/policies", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Policy](resp)
return ret, err
}
// Get get policy info
// See more: https://docs.netbird.io/api/resources/policies#retrieve-a-policy
func (a *PoliciesAPI) Get(ctx context.Context, policyID string) (*api.Policy, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/policies/"+policyID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Policy](resp)
return &ret, err
}
// Create create new policy
// See more: https://docs.netbird.io/api/resources/policies#create-a-policy
func (a *PoliciesAPI) Create(ctx context.Context, request api.PostApiPoliciesJSONRequestBody) (*api.Policy, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/policies", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Policy](resp)
return &ret, err
}
// Update update policy info
// See more: https://docs.netbird.io/api/resources/policies#update-a-policy
func (a *PoliciesAPI) Update(ctx context.Context, policyID string, request api.PutApiPoliciesPolicyIdJSONRequestBody) (*api.Policy, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/policies/"+policyID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Policy](resp)
return &ret, err
}
// Delete delete policy
// See more: https://docs.netbird.io/api/resources/policies#delete-a-policy
func (a *PoliciesAPI) Delete(ctx context.Context, policyID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/policies/"+policyID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,236 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testPolicy = api.Policy{
Name: "wow",
Id: ptr("Test"),
Enabled: false,
}
)
func TestPolicies_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Policy{testPolicy})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Policies.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testPolicy, ret[0])
})
}
func TestPolicies_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies", 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.Policies.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPolicies_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testPolicy)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Policies.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testPolicy, *ret)
})
}
func TestPolicies_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies/Test", 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.Policies.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPolicies_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiPoliciesPolicyIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testPolicy)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Policies.Create(context.Background(), api.PostApiPoliciesJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testPolicy, *ret)
})
}
func TestPolicies_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies", 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.Policies.Create(context.Background(), api.PostApiPoliciesJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPolicies_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiPoliciesPolicyIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testPolicy)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Policies.Update(context.Background(), "Test", api.PutApiPoliciesPolicyIdJSONRequestBody{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testPolicy, *ret)
})
}
func TestPolicies_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies/Test", 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.Policies.Update(context.Background(), "Test", api.PutApiPoliciesPolicyIdJSONRequestBody{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPolicies_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Policies.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestPolicies_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/policies/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Policies.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestPolicies_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
policies, err := c.Policies.List(context.Background())
require.NoError(t, err)
require.NotEmpty(t, policies)
policy, err := c.Policies.Get(context.Background(), *policies[0].Id)
require.NoError(t, err)
assert.Equal(t, *policies[0].Id, *policy.Id)
policy, err = c.Policies.Update(context.Background(), *policy.Id, api.PolicyCreate{
Description: ptr("Test Policy"),
Enabled: false,
Name: "Test",
Rules: []api.PolicyRuleUpdate{
{
Action: api.PolicyRuleUpdateAction(policy.Rules[0].Action),
Bidirectional: true,
Description: ptr("Test Policy"),
Sources: ptr([]string{(*policy.Rules[0].Sources)[0].Id}),
Destinations: ptr([]string{(*policy.Rules[0].Destinations)[0].Id}),
Enabled: false,
Protocol: api.PolicyRuleUpdateProtocolAll,
},
},
SourcePostureChecks: nil,
})
require.NoError(t, err)
assert.Equal(t, "Test Policy", *policy.Rules[0].Description)
policy, err = c.Policies.Create(context.Background(), api.PolicyUpdate{
Description: ptr("Test Policy 2"),
Enabled: false,
Name: "Test",
Rules: []api.PolicyRuleUpdate{
{
Action: api.PolicyRuleUpdateAction(policy.Rules[0].Action),
Bidirectional: true,
Description: ptr("Test Policy 2"),
Sources: ptr([]string{(*policy.Rules[0].Sources)[0].Id}),
Destinations: ptr([]string{(*policy.Rules[0].Destinations)[0].Id}),
Enabled: false,
Protocol: api.PolicyRuleUpdateProtocolAll,
},
},
SourcePostureChecks: nil,
})
require.NoError(t, err)
err = c.Policies.Delete(context.Background(), *policy.Id)
require.NoError(t, err)
})
}

View File

@ -0,0 +1,82 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// PostureChecksAPI APIs for PostureChecks, do not use directly
type PostureChecksAPI struct {
c *Client
}
// List list all posture checks
// See more: https://docs.netbird.io/api/resources/posture-checks#list-all-posture-checks
func (a *PostureChecksAPI) List(ctx context.Context) ([]api.PostureCheck, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/posture-checks", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.PostureCheck](resp)
return ret, err
}
// Get get posture check info
// See more: https://docs.netbird.io/api/resources/posture-checks#retrieve-a-posture-check
func (a *PostureChecksAPI) Get(ctx context.Context, postureCheckID string) (*api.PostureCheck, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/posture-checks/"+postureCheckID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.PostureCheck](resp)
return &ret, err
}
// Create create new posture check
// See more: https://docs.netbird.io/api/resources/posture-checks#create-a-posture-check
func (a *PostureChecksAPI) Create(ctx context.Context, request api.PostApiPostureChecksJSONRequestBody) (*api.PostureCheck, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/posture-checks", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.PostureCheck](resp)
return &ret, err
}
// Update update posture check info
// See more: https://docs.netbird.io/api/resources/posture-checks#update-a-posture-check
func (a *PostureChecksAPI) Update(ctx context.Context, postureCheckID string, request api.PutApiPostureChecksPostureCheckIdJSONRequestBody) (*api.PostureCheck, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/posture-checks/"+postureCheckID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.PostureCheck](resp)
return &ret, err
}
// Delete delete posture check
// See more: https://docs.netbird.io/api/resources/posture-checks#delete-a-posture-check
func (a *PostureChecksAPI) Delete(ctx context.Context, postureCheckID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/posture-checks/"+postureCheckID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,228 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testPostureCheck = api.PostureCheck{
Id: "Test",
Name: "wow",
}
)
func TestPostureChecks_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.PostureCheck{testPostureCheck})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.PostureChecks.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testPostureCheck, ret[0])
})
}
func TestPostureChecks_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks", 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.PostureChecks.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPostureChecks_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testPostureCheck)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.PostureChecks.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testPostureCheck, *ret)
})
}
func TestPostureChecks_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks/Test", 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.PostureChecks.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPostureChecks_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostureCheckUpdate
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testPostureCheck)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.PostureChecks.Create(context.Background(), api.PostureCheckUpdate{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testPostureCheck, *ret)
})
}
func TestPostureChecks_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks", 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.PostureChecks.Create(context.Background(), api.PostureCheckUpdate{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPostureChecks_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostureCheckUpdate
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "weaw", req.Name)
retBytes, _ := json.Marshal(testPostureCheck)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.PostureChecks.Update(context.Background(), "Test", api.PostureCheckUpdate{
Name: "weaw",
})
require.NoError(t, err)
assert.Equal(t, testPostureCheck, *ret)
})
}
func TestPostureChecks_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks/Test", 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.PostureChecks.Update(context.Background(), "Test", api.PostureCheckUpdate{
Name: "weaw",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPostureChecks_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.PostureChecks.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestPostureChecks_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/posture-checks/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.PostureChecks.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestPostureChecks_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
check, err := c.PostureChecks.Create(context.Background(), api.PostureCheckUpdate{
Name: "Test",
Description: "Testing",
Checks: &api.Checks{
OsVersionCheck: &api.OSVersionCheck{
Windows: &api.MinKernelVersionCheck{
MinKernelVersion: "0.0.0",
},
},
},
})
require.NoError(t, err)
assert.Equal(t, "Test", check.Name)
checks, err := c.PostureChecks.List(context.Background())
require.NoError(t, err)
assert.Len(t, checks, 1)
check, err = c.PostureChecks.Update(context.Background(), check.Id, api.PostureCheckUpdate{
Name: "Tests",
Description: "Testings",
Checks: &api.Checks{
GeoLocationCheck: &api.GeoLocationCheck{
Action: api.GeoLocationCheckActionAllow, Locations: []api.Location{
{
CityName: ptr("Cairo"),
CountryCode: "EG",
},
},
},
},
})
require.NoError(t, err)
assert.Equal(t, "Testings", *check.Description)
check, err = c.PostureChecks.Get(context.Background(), check.Id)
require.NoError(t, err)
assert.Equal(t, "Tests", check.Name)
err = c.PostureChecks.Delete(context.Background(), check.Id)
require.NoError(t, err)
})
}

View File

@ -0,0 +1,82 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// RoutesAPI APIs for Routes, do not use directly
type RoutesAPI struct {
c *Client
}
// List list all routes
// See more: https://docs.netbird.io/api/resources/routes#list-all-routes
func (a *RoutesAPI) List(ctx context.Context) ([]api.Route, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/routes", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.Route](resp)
return ret, err
}
// Get get route info
// See more: https://docs.netbird.io/api/resources/routes#retrieve-a-route
func (a *RoutesAPI) Get(ctx context.Context, routeID string) (*api.Route, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/routes/"+routeID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Route](resp)
return &ret, err
}
// Create create new route
// See more: https://docs.netbird.io/api/resources/routes#create-a-route
func (a *RoutesAPI) Create(ctx context.Context, request api.PostApiRoutesJSONRequestBody) (*api.Route, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/routes", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Route](resp)
return &ret, err
}
// Update update route info
// See more: https://docs.netbird.io/api/resources/routes#update-a-route
func (a *RoutesAPI) Update(ctx context.Context, routeID string, request api.PutApiRoutesRouteIdJSONRequestBody) (*api.Route, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/routes/"+routeID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.Route](resp)
return &ret, err
}
// Delete delete route
// See more: https://docs.netbird.io/api/resources/routes#delete-a-route
func (a *RoutesAPI) Delete(ctx context.Context, routeID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/routes/"+routeID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,226 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testRoute = api.Route{
Id: "Test",
Domains: ptr([]string{"google.com"}),
}
)
func TestRoutes_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.Route{testRoute})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Routes.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testRoute, ret[0])
})
}
func TestRoutes_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes", 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.Routes.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestRoutes_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testRoute)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Routes.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testRoute, *ret)
})
}
func TestRoutes_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes/Test", 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.Routes.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestRoutes_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiRoutesJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "meow", req.Description)
retBytes, _ := json.Marshal(testRoute)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Routes.Create(context.Background(), api.PostApiRoutesJSONRequestBody{
Description: "meow",
})
require.NoError(t, err)
assert.Equal(t, testRoute, *ret)
})
}
func TestRoutes_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes", 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.Routes.Create(context.Background(), api.PostApiRoutesJSONRequestBody{
Description: "meow",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestRoutes_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiRoutesRouteIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "meow", req.Description)
retBytes, _ := json.Marshal(testRoute)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Routes.Update(context.Background(), "Test", api.PutApiRoutesRouteIdJSONRequestBody{
Description: "meow",
})
require.NoError(t, err)
assert.Equal(t, testRoute, *ret)
})
}
func TestRoutes_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes/Test", 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.Routes.Update(context.Background(), "Test", api.PutApiRoutesRouteIdJSONRequestBody{
Description: "meow",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestRoutes_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Routes.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestRoutes_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/routes/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Routes.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestRoutes_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
route, err := c.Routes.Create(context.Background(), api.RouteRequest{
Description: "Meow",
Enabled: false,
Groups: []string{"cs1tnh0hhcjnqoiuebeg"},
PeerGroups: ptr([]string{"cs1tnh0hhcjnqoiuebeg"}),
Domains: ptr([]string{"google.com"}),
Masquerade: true,
Metric: 9999,
KeepRoute: false,
NetworkId: "Test",
})
require.NoError(t, err)
assert.Equal(t, "Test", route.NetworkId)
routes, err := c.Routes.List(context.Background())
require.NoError(t, err)
assert.Len(t, routes, 1)
route, err = c.Routes.Update(context.Background(), route.Id, api.RouteRequest{
Description: "Testings",
Enabled: false,
Groups: []string{"cs1tnh0hhcjnqoiuebeg"},
PeerGroups: ptr([]string{"cs1tnh0hhcjnqoiuebeg"}),
Domains: ptr([]string{"google.com"}),
Masquerade: true,
Metric: 9999,
KeepRoute: false,
NetworkId: "Tests",
})
require.NoError(t, err)
assert.Equal(t, "Testings", route.Description)
route, err = c.Routes.Get(context.Background(), route.Id)
require.NoError(t, err)
assert.Equal(t, "Tests", route.NetworkId)
err = c.Routes.Delete(context.Background(), route.Id)
require.NoError(t, err)
})
}

View File

@ -0,0 +1,82 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// SetupKeysAPI APIs for Setup keys, do not use directly
type SetupKeysAPI struct {
c *Client
}
// List list all setup keys
// See more: https://docs.netbird.io/api/resources/setup-keys#list-all-setup-keys
func (a *SetupKeysAPI) List(ctx context.Context) ([]api.SetupKey, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/setup-keys", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.SetupKey](resp)
return ret, err
}
// Get get setup key info
// See more: https://docs.netbird.io/api/resources/setup-keys#retrieve-a-setup-key
func (a *SetupKeysAPI) Get(ctx context.Context, setupKeyID string) (*api.SetupKey, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/setup-keys/"+setupKeyID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.SetupKey](resp)
return &ret, err
}
// Create generate new Setup Key
// See more: https://docs.netbird.io/api/resources/setup-keys#create-a-setup-key
func (a *SetupKeysAPI) Create(ctx context.Context, request api.PostApiSetupKeysJSONRequestBody) (*api.SetupKeyClear, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/setup-keys", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.SetupKeyClear](resp)
return &ret, err
}
// Update generate new Setup Key
// See more: https://docs.netbird.io/api/resources/setup-keys#update-a-setup-key
func (a *SetupKeysAPI) Update(ctx context.Context, setupKeyID string, request api.PutApiSetupKeysKeyIdJSONRequestBody) (*api.SetupKey, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/setup-keys/"+setupKeyID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.SetupKey](resp)
return &ret, err
}
// Delete delete setup key
// See more: https://docs.netbird.io/api/resources/setup-keys#delete-a-setup-key
func (a *SetupKeysAPI) Delete(ctx context.Context, setupKeyID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/setup-keys/"+setupKeyID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,227 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testSetupKey = api.SetupKey{
Id: "Test",
Name: "wow",
AutoGroups: []string{"meow"},
Ephemeral: true,
}
testSteupKeyGenerated = api.SetupKeyClear{
Id: "Test",
Name: "wow",
AutoGroups: []string{"meow"},
Ephemeral: true,
Key: "shhh",
}
)
func TestSetupKeys_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.SetupKey{testSetupKey})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SetupKeys.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testSetupKey, ret[0])
})
}
func TestSetupKeys_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys", 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.SetupKeys.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestSetupKeys_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testSetupKey)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SetupKeys.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testSetupKey, *ret)
})
}
func TestSetupKeys_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys/Test", 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.SetupKeys.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestSetupKeys_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiSetupKeysJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, 5, req.ExpiresIn)
retBytes, _ := json.Marshal(testSteupKeyGenerated)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SetupKeys.Create(context.Background(), api.PostApiSetupKeysJSONRequestBody{
ExpiresIn: 5,
})
require.NoError(t, err)
assert.Equal(t, testSteupKeyGenerated, *ret)
})
}
func TestSetupKeys_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys", 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.SetupKeys.Create(context.Background(), api.PostApiSetupKeysJSONRequestBody{
ExpiresIn: 5,
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestSetupKeys_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiSetupKeysKeyIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, req.Revoked)
retBytes, _ := json.Marshal(testSetupKey)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.SetupKeys.Update(context.Background(), "Test", api.PutApiSetupKeysKeyIdJSONRequestBody{
Revoked: true,
})
require.NoError(t, err)
assert.Equal(t, testSetupKey, *ret)
})
}
func TestSetupKeys_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys/Test", 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.SetupKeys.Update(context.Background(), "Test", api.PutApiSetupKeysKeyIdJSONRequestBody{
Revoked: true,
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestSetupKeys_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.SetupKeys.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestSetupKeys_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/setup-keys/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.SetupKeys.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestSetupKeys_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
group, err := c.Groups.Create(context.Background(), api.GroupRequest{
Name: "Test",
})
require.NoError(t, err)
skClear, err := c.SetupKeys.Create(context.Background(), api.CreateSetupKeyRequest{
AutoGroups: []string{group.Id},
Ephemeral: ptr(false),
Name: "test",
Type: "reusable",
})
require.NoError(t, err)
assert.Equal(t, true, skClear.Valid)
keys, err := c.SetupKeys.List(context.Background())
require.NoError(t, err)
assert.Len(t, keys, 2)
sk, err := c.SetupKeys.Update(context.Background(), skClear.Id, api.SetupKeyRequest{
Revoked: true,
AutoGroups: []string{group.Id},
})
require.NoError(t, err)
sk, err = c.SetupKeys.Get(context.Background(), sk.Id)
require.NoError(t, err)
assert.Equal(t, false, sk.Valid)
err = c.SetupKeys.Delete(context.Background(), sk.Id)
require.NoError(t, err)
})
}

View File

@ -0,0 +1,66 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// TokensAPI APIs for PATs, do not use directly
type TokensAPI struct {
c *Client
}
// List list user tokens
// See more: https://docs.netbird.io/api/resources/tokens#list-all-tokens
func (a *TokensAPI) List(ctx context.Context, userID string) ([]api.PersonalAccessToken, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/users/"+userID+"/tokens", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.PersonalAccessToken](resp)
return ret, err
}
// Get get user token info
// See more: https://docs.netbird.io/api/resources/tokens#retrieve-a-token
func (a *TokensAPI) Get(ctx context.Context, userID, tokenID string) (*api.PersonalAccessToken, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/users/"+userID+"/tokens/"+tokenID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.PersonalAccessToken](resp)
return &ret, err
}
// Create generate new PAT for user
// See more: https://docs.netbird.io/api/resources/tokens#create-a-token
func (a *TokensAPI) Create(ctx context.Context, userID string, request api.PostApiUsersUserIdTokensJSONRequestBody) (*api.PersonalAccessTokenGenerated, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/users/"+userID+"/tokens", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.PersonalAccessTokenGenerated](resp)
return &ret, err
}
// Delete delete user token
// See more: https://docs.netbird.io/api/resources/tokens#delete-a-token
func (a *TokensAPI) Delete(ctx context.Context, userID, tokenID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/users/"+userID+"/tokens/"+tokenID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,175 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testToken = api.PersonalAccessToken{
Id: "Test",
CreatedAt: time.Time{},
CreatedBy: "meow",
ExpirationDate: time.Time{},
LastUsed: nil,
Name: "wow",
}
testTokenGenerated = api.PersonalAccessTokenGenerated{
PersonalAccessToken: testToken,
PlainToken: "shhh",
}
)
func TestTokens_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.PersonalAccessToken{testToken})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Tokens.List(context.Background(), "meow")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testToken, ret[0])
})
}
func TestTokens_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens", 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.Tokens.List(context.Background(), "meow")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestTokens_Get_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testToken)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Tokens.Get(context.Background(), "meow", "Test")
require.NoError(t, err)
assert.Equal(t, testToken, *ret)
})
}
func TestTokens_Get_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens/Test", 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.Tokens.Get(context.Background(), "meow", "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestTokens_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiUsersUserIdTokensJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, 5, req.ExpiresIn)
retBytes, _ := json.Marshal(testTokenGenerated)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Tokens.Create(context.Background(), "meow", api.PostApiUsersUserIdTokensJSONRequestBody{
ExpiresIn: 5,
})
require.NoError(t, err)
assert.Equal(t, testTokenGenerated, *ret)
})
}
func TestTokens_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens", 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.Tokens.Create(context.Background(), "meow", api.PostApiUsersUserIdTokensJSONRequestBody{
ExpiresIn: 5,
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestTokens_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Tokens.Delete(context.Background(), "meow", "Test")
require.NoError(t, err)
})
}
func TestTokens_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/meow/tokens/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Tokens.Delete(context.Background(), "meow", "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestTokens_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
tokenClear, err := c.Tokens.Create(context.Background(), "a23efe53-63fb-11ec-90d6-0242ac120003", api.PersonalAccessTokenRequest{
Name: "Test",
ExpiresIn: 365,
})
require.NoError(t, err)
assert.Equal(t, "Test", tokenClear.PersonalAccessToken.Name)
tokens, err := c.Tokens.List(context.Background(), "a23efe53-63fb-11ec-90d6-0242ac120003")
require.NoError(t, err)
assert.Len(t, tokens, 2)
token, err := c.Tokens.Get(context.Background(), "a23efe53-63fb-11ec-90d6-0242ac120003", tokenClear.PersonalAccessToken.Id)
require.NoError(t, err)
assert.Equal(t, "Test", token.Name)
err = c.Tokens.Delete(context.Background(), "a23efe53-63fb-11ec-90d6-0242ac120003", token.Id)
require.NoError(t, err)
})
}

View File

@ -0,0 +1,82 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/management/server/http/api"
)
// UsersAPI APIs for users, do not use directly
type UsersAPI struct {
c *Client
}
// List list all users, only returns one user always
// See more: https://docs.netbird.io/api/resources/users#list-all-users
func (a *UsersAPI) List(ctx context.Context) ([]api.User, error) {
resp, err := a.c.newRequest(ctx, "GET", "/api/users", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[[]api.User](resp)
return ret, err
}
// Create create user
// See more: https://docs.netbird.io/api/resources/users#create-a-user
func (a *UsersAPI) Create(ctx context.Context, request api.PostApiUsersJSONRequestBody) (*api.User, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "POST", "/api/users", bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.User](resp)
return &ret, err
}
// Update update user settings
// See more: https://docs.netbird.io/api/resources/users#update-a-user
func (a *UsersAPI) Update(ctx context.Context, userID string, request api.PutApiUsersUserIdJSONRequestBody) (*api.User, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.newRequest(ctx, "PUT", "/api/users/"+userID, bytes.NewReader(requestBytes))
if err != nil {
return nil, err
}
defer resp.Body.Close()
ret, err := parseResponse[api.User](resp)
return &ret, err
}
// Delete delete user
// See more: https://docs.netbird.io/api/resources/users#delete-a-user
func (a *UsersAPI) Delete(ctx context.Context, userID string) error {
resp, err := a.c.newRequest(ctx, "DELETE", "/api/users/"+userID, nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
// ResendInvitation resend user invitation
// See more: https://docs.netbird.io/api/resources/users#resend-user-invitation
func (a *UsersAPI) ResendInvitation(ctx context.Context, userID string) error {
resp, err := a.c.newRequest(ctx, "POST", "/api/users/"+userID+"/invite", nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -0,0 +1,222 @@
package rest
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
testUser = api.User{
Id: "Test",
AutoGroups: []string{"test-group"},
Email: "test@test.com",
IsBlocked: false,
IsCurrent: ptr(false),
IsServiceUser: ptr(false),
Issued: ptr("api"),
LastLogin: &time.Time{},
Name: "M. Essam",
Permissions: &api.UserPermissions{
DashboardView: ptr(api.UserPermissionsDashboardViewFull),
},
Role: "user",
Status: api.UserStatusActive,
}
)
func TestUsers_List_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.User{testUser})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testUser, ret[0])
})
}
func TestUsers_List_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users", 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.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestUsers_Create_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiUsersJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, []string{"meow"}, req.AutoGroups)
retBytes, _ := json.Marshal(testUser)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.Create(context.Background(), api.PostApiUsersJSONRequestBody{
AutoGroups: []string{"meow"},
})
require.NoError(t, err)
assert.Equal(t, testUser, *ret)
})
}
func TestUsers_Create_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users", 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.Create(context.Background(), api.PostApiUsersJSONRequestBody{
AutoGroups: []string{"meow"},
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestUsers_Update_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiUsersUserIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, req.IsBlocked)
retBytes, _ := json.Marshal(testUser)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Users.Update(context.Background(), "Test", api.PutApiUsersUserIdJSONRequestBody{
IsBlocked: true,
})
require.NoError(t, err)
assert.Equal(t, testUser, *ret)
})
}
func TestUsers_Update_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test", 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.Update(context.Background(), "Test", api.PutApiUsersUserIdJSONRequestBody{
IsBlocked: true,
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestUsers_Delete_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Users.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestUsers_Delete_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Users.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestUsers_ResendInvitation_200(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/invite", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
w.WriteHeader(200)
})
err := c.Users.ResendInvitation(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestUsers_ResendInvitation_Err(t *testing.T) {
withMockClient(func(c *Client, mux *http.ServeMux) {
mux.HandleFunc("/api/users/Test/invite", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.Users.ResendInvitation(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestUsers_Integration(t *testing.T) {
withBlackBoxServer(t, func(c *Client) {
user, err := c.Users.Create(context.Background(), api.UserCreateRequest{
AutoGroups: []string{},
Email: ptr("test@example.com"),
IsServiceUser: true,
Name: ptr("Nobody"),
Role: "user",
})
require.NoError(t, err)
assert.Equal(t, "Nobody", user.Name)
users, err := c.Users.List(context.Background())
require.NoError(t, err)
assert.NotEmpty(t, users)
user, err = c.Users.Update(context.Background(), user.Id, api.UserRequest{
AutoGroups: []string{},
Role: "admin",
})
require.NoError(t, err)
assert.Equal(t, "admin", user.Role)
err = c.Users.Delete(context.Background(), user.Id)
require.NoError(t, err)
})
}

View File

@ -7,15 +7,14 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server/http/configs"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/configs"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
)
// handler is a handler that returns groups of the account

View File

@ -14,7 +14,7 @@ import (
"testing"
"github.com/gorilla/mux"
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/maps"
"github.com/netbirdio/netbird/management/server"

View File

@ -10,17 +10,14 @@ import (
"strings"
"testing"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/mock_server"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/magiconair/properties/assert"
"github.com/netbirdio/netbird/management/server/mock_server"
)
func initPoliciesTestData(policies ...*types.Policy) *handler {

View File

@ -11,21 +11,19 @@ import (
"net/netip"
"testing"
"github.com/netbirdio/netbird/management/server/util"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/mock_server"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server/util"
"github.com/netbirdio/netbird/route"
"github.com/gorilla/mux"
"github.com/magiconair/properties/assert"
"github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/mock_server"
)
const (

View File

@ -19,6 +19,7 @@ CREATE INDEX `idx_accounts_domain` ON `accounts`(`domain`);
CREATE INDEX `idx_setup_keys_account_id` ON `setup_keys`(`account_id`);
CREATE INDEX `idx_peers_key` ON `peers`(`key`);
CREATE INDEX `idx_peers_account_id` ON `peers`(`account_id`);
CREATE INDEX `idx_peers_account_id_ip` ON `peers`(`account_id`,`ip`);
CREATE INDEX `idx_users_account_id` ON `users`(`account_id`);
CREATE INDEX `idx_personal_access_tokens_user_id` ON `personal_access_tokens`(`user_id`);
CREATE INDEX `idx_groups_account_id` ON `groups`(`account_id`);
@ -39,8 +40,11 @@ CREATE INDEX `idx_networks_account_id` ON `networks`(`account_id`);
INSERT INTO accounts VALUES('bf1c8084-ba50-4ce7-9439-34653001fc3b','','2024-10-02 16:03:06.778746+02:00','test.com','private',1,'af1c8024-ha40-4ce2-9418-34653101fc3c','{"IP":"100.64.0.0","Mask":"//8AAA=="}','',0,'[]',0,86400000000000,0,0,0,'',NULL,NULL,NULL);
INSERT INTO "groups" VALUES('cs1tnh0hhcjnqoiuebeg','bf1c8084-ba50-4ce7-9439-34653001fc3b','All','api','[]',0,'');
INSERT INTO setup_keys VALUES('','bf1c8084-ba50-4ce7-9439-34653001fc3b','A2C8E62B-38F5-4553-B31E-DD66C696CEBB','Default key','reusable','2021-08-19 20:46:20.005936822+02:00','2321-09-18 20:46:20.005936822+02:00','2021-08-19 20:46:20.005936822+02:00',0,0,NULL,'["cs1tnh0hhcjnqoiuebeg"]',0,0);
INSERT INTO users VALUES('a23efe53-63fb-11ec-90d6-0242ac120003','bf1c8084-ba50-4ce7-9439-34653001fc3b','owner',0,0,'','[]',0,NULL,'2024-10-02 16:03:06.779156+02:00','api',0,'');
INSERT INTO users VALUES('edafee4e-63fb-11ec-90d6-0242ac120003','bf1c8084-ba50-4ce7-9439-34653001fc3b','admin',0,0,'','[]',0,NULL,'2024-10-02 16:03:06.779156+02:00','api',0,'');
INSERT INTO users VALUES('f4f6d672-63fb-11ec-90d6-0242ac120003','bf1c8084-ba50-4ce7-9439-34653001fc3b','user',0,0,'','[]',0,NULL,'2024-10-02 16:03:06.779156+02:00','api',0,'');
-- Unhashed PAT is "nbp_apTmlmUXHSC4PKmHwtIZNaGr8eqcVI2gMURp"
INSERT INTO personal_access_tokens VALUES('9dj38s35-63fb-11ec-90d6-0242ac120004','a23efe53-63fb-11ec-90d6-0242ac120003','','smJvzexPcQ3NRezrVDUmF++0XqvFvXzx8Rsn2y9r1z0=','5023-02-27 00:00:00+00:00','user','2023-01-01 00:00:00+00:00','2023-02-01 00:00:00+00:00');
INSERT INTO personal_access_tokens VALUES('9dj38s35-63fb-11ec-90d6-0242ac120003','f4f6d672-63fb-11ec-90d6-0242ac120003','','SoMeHaShEdToKeN','2023-02-27 00:00:00+00:00','user','2023-01-01 00:00:00+00:00','2023-02-01 00:00:00+00:00');
INSERT INTO installations VALUES(1,'');
INSERT INTO policies VALUES('cs1tnh0hhcjnqoiuebf0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Default','This is a default rule that allows connections between all the resources',1,'[]');
@ -48,3 +52,4 @@ INSERT INTO policy_rules VALUES('cs387mkv2d4bgq41b6n0','cs1tnh0hhcjnqoiuebf0','D
INSERT INTO network_routers VALUES('ctc20ji7qv9ck2sebc80','ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','cs1tnh0hhcjnqoiuebeg',NULL,0,0);
INSERT INTO network_resources VALUES ('ctc4nci7qv9061u6ilfg','ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Host','192.168.1.1');
INSERT INTO networks VALUES('ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Test Network','Test Network');
INSERT INTO peers VALUES('ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','','','192.168.0.0','','','','','','','','','','','','','','','','','test','test','2023-01-01 00:00:00+00:00',0,0,0,'a23efe53-63fb-11ec-90d6-0242ac120003','',0,0,'2023-01-01 00:00:00+00:00','2023-01-01 00:00:00+00:00',0,'','','',0);

View File

@ -2,6 +2,8 @@ package ws
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
@ -13,6 +15,7 @@ import (
"nhooyr.io/websocket"
"github.com/netbirdio/netbird/relay/server/listener/ws"
"github.com/netbirdio/netbird/util/embeddedroots"
nbnet "github.com/netbirdio/netbird/util/net"
)
@ -66,10 +69,19 @@ func prepareURL(address string) (string, error) {
func httpClientNbDialer() *http.Client {
customDialer := nbnet.NewDialer()
certPool, err := x509.SystemCertPool()
if err != nil || certPool == nil {
log.Debugf("System cert pool not available; falling back to embedded cert, error: %v", err)
certPool = embeddedroots.Get()
}
customTransport := &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return customDialer.DialContext(ctx, network, addr)
},
TLSClientConfig: &tls.Config{
RootCAs: certPool,
},
}
return &http.Client{

View File

@ -2,11 +2,25 @@
package tls
import "crypto/tls"
import (
"crypto/tls"
"crypto/x509"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/util/embeddedroots"
)
func ClientQUICTLSConfig() *tls.Config {
certPool, err := x509.SystemCertPool()
if err != nil || certPool == nil {
log.Debugf("System cert pool not available; falling back to embedded cert, error: %v", err)
certPool = embeddedroots.Get()
}
return &tls.Config{
InsecureSkipVerify: true, // Debug mode allows insecure connections
NextProtos: []string{nbalpn}, // Ensure this matches the server's ALPN
RootCAs: certPool,
}
}

View File

@ -2,10 +2,24 @@
package tls
import "crypto/tls"
import (
"crypto/tls"
"crypto/x509"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/util/embeddedroots"
)
func ClientQUICTLSConfig() *tls.Config {
certPool, err := x509.SystemCertPool()
if err != nil || certPool == nil {
log.Debugf("System cert pool not available; falling back to embedded cert, error: %v", err)
certPool = embeddedroots.Get()
}
return &tls.Config{
NextProtos: []string{nbalpn},
RootCAs: certPool,
}
}

View File

@ -0,0 +1,42 @@
package embeddedroots
import (
"crypto/x509"
_ "embed"
"sync"
)
func Get() *x509.CertPool {
rootsVar.load()
return rootsVar.p
}
type roots struct {
once sync.Once
p *x509.CertPool
}
var rootsVar roots
func (r *roots) load() {
r.once.Do(func() {
p := x509.NewCertPool()
p.AppendCertsFromPEM([]byte(isrgRootX1RootPEM))
p.AppendCertsFromPEM([]byte(isrgRootX2RootPEM))
r.p = p
})
}
// Subject: O = Internet Security Research Group, CN = ISRG Root X1
// Key type: RSA 4096
// Validity: until 2030-06-04 (generated 2015-06-04)
//
//go:embed isrg-root-x1.pem
var isrgRootX1RootPEM string
// Subject: O = Internet Security Research Group, CN = ISRG Root X2
// Key type: ECDSA P-384
// Validity: until 2035-09-04 (generated 2020-09-04)
//
//go:embed isrg-root-x2.pem
var isrgRootX2RootPEM string

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
/q4AaOeMSQ+2b1tbFfLn
-----END CERTIFICATE-----

View File

@ -3,14 +3,16 @@ package grpc
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"net"
"os/user"
"runtime"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/cenkalti/backoff/v4"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
@ -18,6 +20,7 @@ import (
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/keepalive"
"github.com/netbirdio/netbird/util/embeddedroots"
nbnet "github.com/netbirdio/netbird/util/net"
)
@ -57,9 +60,16 @@ func Backoff(ctx context.Context) backoff.BackOff {
func CreateConnection(addr string, tlsEnabled bool) (*grpc.ClientConn, error) {
transportOption := grpc.WithTransportCredentials(insecure.NewCredentials())
if tlsEnabled {
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
certPool, err := x509.SystemCertPool()
if err != nil || certPool == nil {
log.Debugf("System cert pool not available; falling back to embedded cert, error: %v", err)
certPool = embeddedroots.Get()
}
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
RootCAs: certPool,
}))
}
connCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)