From f930ef2ee608b26d6e924c26e5cfe3085404b739 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Mon, 3 Feb 2025 17:54:35 +0100 Subject: [PATCH 1/9] Cleanup magiconair usage from repo (#3276) --- client/internal/peer/conn_status_test.go | 3 ++- client/internal/peer/conn_test.go | 2 +- go.mod | 2 +- .../server/http/handlers/groups/groups_handler.go | 7 +++---- .../http/handlers/groups/groups_handler_test.go | 2 +- .../handlers/policies/policies_handler_test.go | 13 +++++-------- .../http/handlers/routes/routes_handler_test.go | 14 ++++++-------- 7 files changed, 19 insertions(+), 24 deletions(-) diff --git a/client/internal/peer/conn_status_test.go b/client/internal/peer/conn_status_test.go index 564fd41c1..6088df55d 100644 --- a/client/internal/peer/conn_status_test.go +++ b/client/internal/peer/conn_status_test.go @@ -1,8 +1,9 @@ package peer import ( - "github.com/magiconair/properties/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestConnStatus_String(t *testing.T) { diff --git a/client/internal/peer/conn_test.go b/client/internal/peer/conn_test.go index b3e9d5b60..505bedb7f 100644 --- a/client/internal/peer/conn_test.go +++ b/client/internal/peer/conn_test.go @@ -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" diff --git a/go.mod b/go.mod index e65296a53..13adeff09 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/management/server/http/handlers/groups/groups_handler.go b/management/server/http/handlers/groups/groups_handler.go index b7121c234..ec635a358 100644 --- a/management/server/http/handlers/groups/groups_handler.go +++ b/management/server/http/handlers/groups/groups_handler.go @@ -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 diff --git a/management/server/http/handlers/groups/groups_handler_test.go b/management/server/http/handlers/groups/groups_handler_test.go index 96e381da1..0668982f3 100644 --- a/management/server/http/handlers/groups/groups_handler_test.go +++ b/management/server/http/handlers/groups/groups_handler_test.go @@ -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" diff --git a/management/server/http/handlers/policies/policies_handler_test.go b/management/server/http/handlers/policies/policies_handler_test.go index 3e1be187c..8fbf84d4b 100644 --- a/management/server/http/handlers/policies/policies_handler_test.go +++ b/management/server/http/handlers/policies/policies_handler_test.go @@ -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 { diff --git a/management/server/http/handlers/routes/routes_handler_test.go b/management/server/http/handlers/routes/routes_handler_test.go index 45c465587..4064ec361 100644 --- a/management/server/http/handlers/routes/routes_handler_test.go +++ b/management/server/http/handlers/routes/routes_handler_test.go @@ -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 ( From 7d385b8dc327bcd3ba67b300ad64e937dd49a251 Mon Sep 17 00:00:00 2001 From: "M. Essam" Date: Tue, 4 Feb 2025 12:10:10 +0200 Subject: [PATCH 2/9] [management] REST client package (#3278) --- management/client/rest/accounts.go | 54 ++ management/client/rest/accounts_test.go | 169 ++++++ management/client/rest/client.go | 133 +++++ management/client/rest/client_test.go | 30 + management/client/rest/dns.go | 110 ++++ management/client/rest/dns_test.go | 295 ++++++++++ management/client/rest/events.go | 24 + management/client/rest/events_test.go | 65 ++ management/client/rest/geo.go | 36 ++ management/client/rest/geo_test.go | 96 +++ management/client/rest/groups.go | 82 +++ management/client/rest/groups_test.go | 210 +++++++ management/client/rest/networks.go | 246 ++++++++ management/client/rest/networks_test.go | 589 +++++++++++++++++++ management/client/rest/peers.go | 78 +++ management/client/rest/peers_test.go | 203 +++++++ management/client/rest/policies.go | 82 +++ management/client/rest/policies_test.go | 236 ++++++++ management/client/rest/posturechecks.go | 82 +++ management/client/rest/posturechecks_test.go | 228 +++++++ management/client/rest/routes.go | 82 +++ management/client/rest/routes_test.go | 226 +++++++ management/client/rest/setupkeys.go | 82 +++ management/client/rest/setupkeys_test.go | 227 +++++++ management/client/rest/tokens.go | 66 +++ management/client/rest/tokens_test.go | 175 ++++++ management/client/rest/users.go | 82 +++ management/client/rest/users_test.go | 222 +++++++ management/server/testdata/store.sql | 5 + 29 files changed, 4215 insertions(+) create mode 100644 management/client/rest/accounts.go create mode 100644 management/client/rest/accounts_test.go create mode 100644 management/client/rest/client.go create mode 100644 management/client/rest/client_test.go create mode 100644 management/client/rest/dns.go create mode 100644 management/client/rest/dns_test.go create mode 100644 management/client/rest/events.go create mode 100644 management/client/rest/events_test.go create mode 100644 management/client/rest/geo.go create mode 100644 management/client/rest/geo_test.go create mode 100644 management/client/rest/groups.go create mode 100644 management/client/rest/groups_test.go create mode 100644 management/client/rest/networks.go create mode 100644 management/client/rest/networks_test.go create mode 100644 management/client/rest/peers.go create mode 100644 management/client/rest/peers_test.go create mode 100644 management/client/rest/policies.go create mode 100644 management/client/rest/policies_test.go create mode 100644 management/client/rest/posturechecks.go create mode 100644 management/client/rest/posturechecks_test.go create mode 100644 management/client/rest/routes.go create mode 100644 management/client/rest/routes_test.go create mode 100644 management/client/rest/setupkeys.go create mode 100644 management/client/rest/setupkeys_test.go create mode 100644 management/client/rest/tokens.go create mode 100644 management/client/rest/tokens_test.go create mode 100644 management/client/rest/users.go create mode 100644 management/client/rest/users_test.go diff --git a/management/client/rest/accounts.go b/management/client/rest/accounts.go new file mode 100644 index 000000000..f38b19f70 --- /dev/null +++ b/management/client/rest/accounts.go @@ -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 +} diff --git a/management/client/rest/accounts_test.go b/management/client/rest/accounts_test.go new file mode 100644 index 000000000..3c1925fbc --- /dev/null +++ b/management/client/rest/accounts_test.go @@ -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) +// }) +// } diff --git a/management/client/rest/client.go b/management/client/rest/client.go new file mode 100644 index 000000000..f55e2d11e --- /dev/null +++ b/management/client/rest/client.go @@ -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 +} diff --git a/management/client/rest/client_test.go b/management/client/rest/client_test.go new file mode 100644 index 000000000..a42b12fa3 --- /dev/null +++ b/management/client/rest/client_test.go @@ -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) +} diff --git a/management/client/rest/dns.go b/management/client/rest/dns.go new file mode 100644 index 000000000..ef9923b1f --- /dev/null +++ b/management/client/rest/dns.go @@ -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 +} diff --git a/management/client/rest/dns_test.go b/management/client/rest/dns_test.go new file mode 100644 index 000000000..d2c00549c --- /dev/null +++ b/management/client/rest/dns_test.go @@ -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) + }) +} diff --git a/management/client/rest/events.go b/management/client/rest/events.go new file mode 100644 index 000000000..1157700ff --- /dev/null +++ b/management/client/rest/events.go @@ -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 +} diff --git a/management/client/rest/events_test.go b/management/client/rest/events_test.go new file mode 100644 index 000000000..515c227e6 --- /dev/null +++ b/management/client/rest/events_test.go @@ -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) + }) +} diff --git a/management/client/rest/geo.go b/management/client/rest/geo.go new file mode 100644 index 000000000..ed9090fe2 --- /dev/null +++ b/management/client/rest/geo.go @@ -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 +} diff --git a/management/client/rest/geo_test.go b/management/client/rest/geo_test.go new file mode 100644 index 000000000..dd42ecba8 --- /dev/null +++ b/management/client/rest/geo_test.go @@ -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) + }) +} diff --git a/management/client/rest/groups.go b/management/client/rest/groups.go new file mode 100644 index 000000000..feb664273 --- /dev/null +++ b/management/client/rest/groups.go @@ -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 +} diff --git a/management/client/rest/groups_test.go b/management/client/rest/groups_test.go new file mode 100644 index 000000000..ac534437d --- /dev/null +++ b/management/client/rest/groups_test.go @@ -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) + }) +} diff --git a/management/client/rest/networks.go b/management/client/rest/networks.go new file mode 100644 index 000000000..2cdd6d73d --- /dev/null +++ b/management/client/rest/networks.go @@ -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 +} diff --git a/management/client/rest/networks_test.go b/management/client/rest/networks_test.go new file mode 100644 index 000000000..934c55380 --- /dev/null +++ b/management/client/rest/networks_test.go @@ -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) + }) +} diff --git a/management/client/rest/peers.go b/management/client/rest/peers.go new file mode 100644 index 000000000..9d35f013c --- /dev/null +++ b/management/client/rest/peers.go @@ -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 +} diff --git a/management/client/rest/peers_test.go b/management/client/rest/peers_test.go new file mode 100644 index 000000000..216ee990c --- /dev/null +++ b/management/client/rest/peers_test.go @@ -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) + }) +} diff --git a/management/client/rest/policies.go b/management/client/rest/policies.go new file mode 100644 index 000000000..be6abafaf --- /dev/null +++ b/management/client/rest/policies.go @@ -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 +} diff --git a/management/client/rest/policies_test.go b/management/client/rest/policies_test.go new file mode 100644 index 000000000..f7fc6ff10 --- /dev/null +++ b/management/client/rest/policies_test.go @@ -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) + }) +} diff --git a/management/client/rest/posturechecks.go b/management/client/rest/posturechecks.go new file mode 100644 index 000000000..950d17ba0 --- /dev/null +++ b/management/client/rest/posturechecks.go @@ -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 +} diff --git a/management/client/rest/posturechecks_test.go b/management/client/rest/posturechecks_test.go new file mode 100644 index 000000000..6fefc0140 --- /dev/null +++ b/management/client/rest/posturechecks_test.go @@ -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) + }) +} diff --git a/management/client/rest/routes.go b/management/client/rest/routes.go new file mode 100644 index 000000000..bccbb8847 --- /dev/null +++ b/management/client/rest/routes.go @@ -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 +} diff --git a/management/client/rest/routes_test.go b/management/client/rest/routes_test.go new file mode 100644 index 000000000..123bd41d4 --- /dev/null +++ b/management/client/rest/routes_test.go @@ -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) + }) +} diff --git a/management/client/rest/setupkeys.go b/management/client/rest/setupkeys.go new file mode 100644 index 000000000..645614fcf --- /dev/null +++ b/management/client/rest/setupkeys.go @@ -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 +} diff --git a/management/client/rest/setupkeys_test.go b/management/client/rest/setupkeys_test.go new file mode 100644 index 000000000..82c3d1fc8 --- /dev/null +++ b/management/client/rest/setupkeys_test.go @@ -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) + }) +} diff --git a/management/client/rest/tokens.go b/management/client/rest/tokens.go new file mode 100644 index 000000000..3275bea81 --- /dev/null +++ b/management/client/rest/tokens.go @@ -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 +} diff --git a/management/client/rest/tokens_test.go b/management/client/rest/tokens_test.go new file mode 100644 index 000000000..478fae93e --- /dev/null +++ b/management/client/rest/tokens_test.go @@ -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) + }) +} diff --git a/management/client/rest/users.go b/management/client/rest/users.go new file mode 100644 index 000000000..372bcee45 --- /dev/null +++ b/management/client/rest/users.go @@ -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 +} diff --git a/management/client/rest/users_test.go b/management/client/rest/users_test.go new file mode 100644 index 000000000..aaec3bf42 --- /dev/null +++ b/management/client/rest/users_test.go @@ -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) + }) +} diff --git a/management/server/testdata/store.sql b/management/server/testdata/store.sql index 17f029713..1c0767bde 100644 --- a/management/server/testdata/store.sql +++ b/management/server/testdata/store.sql @@ -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); From 0125cd97d8d8d68ce3638ebae1e8618008a6acfb Mon Sep 17 00:00:00 2001 From: hakansa <43675540+hakansa@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:17:59 +0300 Subject: [PATCH 3/9] [client] use embedded root CA if system certpool is empty (#3272) * Implement custom TLS certificate handling with fallback to embedded roots --- client/internal/auth/device_flow.go | 17 ++++++++++++ relay/client/dialer/ws/ws.go | 12 +++++++++ relay/tls/client_dev.go | 16 ++++++++++- relay/tls/client_prod.go | 16 ++++++++++- util/embeddedroots/embeddedroots.go | 42 +++++++++++++++++++++++++++++ util/embeddedroots/isrg-root-x1.pem | 31 +++++++++++++++++++++ util/embeddedroots/isrg-root-x2.pem | 14 ++++++++++ util/grpc/dialer.go | 18 ++++++++++--- 8 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 util/embeddedroots/embeddedroots.go create mode 100644 util/embeddedroots/isrg-root-x1.pem create mode 100644 util/embeddedroots/isrg-root-x2.pem diff --git a/client/internal/auth/device_flow.go b/client/internal/auth/device_flow.go index 87d00de5e..da4f16c8d 100644 --- a/client/internal/auth/device_flow.go +++ b/client/internal/auth/device_flow.go @@ -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, diff --git a/relay/client/dialer/ws/ws.go b/relay/client/dialer/ws/ws.go index df91a66d4..2adbd2451 100644 --- a/relay/client/dialer/ws/ws.go +++ b/relay/client/dialer/ws/ws.go @@ -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{ diff --git a/relay/tls/client_dev.go b/relay/tls/client_dev.go index f6b8290a0..52e5535c5 100644 --- a/relay/tls/client_dev.go +++ b/relay/tls/client_dev.go @@ -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, } } diff --git a/relay/tls/client_prod.go b/relay/tls/client_prod.go index 686093a37..62e218bc3 100644 --- a/relay/tls/client_prod.go +++ b/relay/tls/client_prod.go @@ -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, } } diff --git a/util/embeddedroots/embeddedroots.go b/util/embeddedroots/embeddedroots.go new file mode 100644 index 000000000..d205f5b69 --- /dev/null +++ b/util/embeddedroots/embeddedroots.go @@ -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 diff --git a/util/embeddedroots/isrg-root-x1.pem b/util/embeddedroots/isrg-root-x1.pem new file mode 100644 index 000000000..57d4a3766 --- /dev/null +++ b/util/embeddedroots/isrg-root-x1.pem @@ -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----- \ No newline at end of file diff --git a/util/embeddedroots/isrg-root-x2.pem b/util/embeddedroots/isrg-root-x2.pem new file mode 100644 index 000000000..7d903edc9 --- /dev/null +++ b/util/embeddedroots/isrg-root-x2.pem @@ -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----- diff --git a/util/grpc/dialer.go b/util/grpc/dialer.go index 4fbffe342..83a11c65d 100644 --- a/util/grpc/dialer.go +++ b/util/grpc/dialer.go @@ -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) From 97d498c59cc1d0f86cb82e5b8e1e89a44365b772 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:49:41 +0100 Subject: [PATCH 4/9] [misc, client, management] Replace Wiretrustee with Netbird (#3267) --- .github/workflows/release.yml | 2 +- AUTHORS | 2 +- CONTRIBUTOR_LICENSE_AGREEMENT.md | 48 +- LICENSE | 4 +- client/iface/configurer/name.go | 2 +- client/iface/configurer/name_darwin.go | 2 +- client/iface/device/device_android.go | 2 +- client/iface/device/device_ios.go | 2 +- client/internal/connect.go | 16 +- client/internal/engine.go | 4 +- client/netbird.wxs | 4 +- client/proto/daemon.pb.go | 4 +- client/proto/daemon.proto | 2 +- client/system/info.go | 2 +- client/system/info_android.go | 2 +- client/system/info_darwin.go | 2 +- client/system/info_freebsd.go | 22 +- client/system/info_ios.go | 2 +- client/system/info_linux.go | 2 +- client/system/info_test.go | 2 +- client/system/info_windows.go | 2 +- management/client/client_test.go | 24 +- management/client/grpc.go | 28 +- management/proto/management.pb.go | 934 ++++++++++----------- management/proto/management.proto | 22 +- management/server/config.go | 4 +- management/server/grpcserver.go | 16 +- management/server/management_proto_test.go | 98 +-- management/server/management_test.go | 42 +- management/server/peer_test.go | 14 +- management/server/testdata/management.json | 8 +- management/server/token_mgr.go | 6 +- management/server/token_mgr_test.go | 8 +- release_files/darwin-ui-installer.sh | 12 +- release_files/install.sh | 12 +- signal/README.md | 2 +- 36 files changed, 679 insertions(+), 681 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8f267ebdd..04874bdf4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ env: SIGN_PIPE_VER: "v0.0.18" GORELEASER_VER: "v2.3.2" PRODUCT_NAME: "NetBird" - COPYRIGHT: "Wiretrustee UG (haftungsbeschreankt)" + COPYRIGHT: "NetBird GmbH" concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }} diff --git a/AUTHORS b/AUTHORS index f2b228766..f39620acc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,3 @@ Mikhail Bragin (https://github.com/braginini) Maycon Santos (https://github.com/mlsmaycon) -Wiretrustee UG (haftungsbeschränkt) +NetBird GmbH diff --git a/CONTRIBUTOR_LICENSE_AGREEMENT.md b/CONTRIBUTOR_LICENSE_AGREEMENT.md index c47eb813d..89e011ec1 100644 --- a/CONTRIBUTOR_LICENSE_AGREEMENT.md +++ b/CONTRIBUTOR_LICENSE_AGREEMENT.md @@ -3,10 +3,10 @@ We are incredibly thankful for the contributions we receive from the community. We require our external contributors to sign a Contributor License Agreement ("CLA") in order to ensure that our projects remain licensed under Free and Open Source licenses such -as BSD-3 while allowing Wiretrustee to build a sustainable business. +as BSD-3 while allowing NetBird to build a sustainable business. -Wiretrustee is committed to having a true Open Source Software ("OSS") license for -our software. A CLA enables Wiretrustee to safely commercialize our products +NetBird is committed to having a true Open Source Software ("OSS") license for +our software. A CLA enables NetBird to safely commercialize our products while keeping a standard OSS license with all the rights that license grants to users: the ability to use the project in their own projects or businesses, to republish modified source, or to completely fork the project. @@ -20,11 +20,11 @@ This is a human-readable summary of (and not a substitute for) the full agreemen This highlights only some of key terms of the CLA. It has no legal value and you should carefully review all the terms of the actual CLA before agreeing. -
  • Grant of copyright license. You give Wiretrustee permission to use your copyrighted work +
  • Grant of copyright license. You give NetBird permission to use your copyrighted work in commercial products.
  • -
  • Grant of patent license. If your contributed work uses a patent, you give Wiretrustee a +
  • Grant of patent license. If your contributed work uses a patent, you give NetBird a license to use that patent including within commercial products. You also agree that you have permission to grant this license.
  • @@ -45,7 +45,7 @@ more. # Why require a CLA? Agreeing to a CLA explicitly states that you are entitled to provide a contribution, that you cannot withdraw permission -to use your contribution at a later date, and that Wiretrustee has permission to use your contribution in our commercial +to use your contribution at a later date, and that NetBird has permission to use your contribution in our commercial products. This removes any ambiguities or uncertainties caused by not having a CLA and allows users and customers to confidently @@ -65,25 +65,25 @@ Follow the steps given by the bot to sign the CLA. This will require you to log information from your account) and to fill in a few additional details such as your name and email address. We will only use this information for CLA tracking; none of your submitted information will be used for marketing purposes. -You only have to sign the CLA once. Once you've signed the CLA, future contributions to any Wiretrustee project will not +You only have to sign the CLA once. Once you've signed the CLA, future contributions to any NetBird project will not require you to sign again. # Legal Terms and Agreement -In order to clarify the intellectual property license granted with Contributions from any person or entity, Wiretrustee -UG (haftungsbeschränkt) ("Wiretrustee") must have a Contributor License Agreement ("CLA") on file that has been signed +In order to clarify the intellectual property license granted with Contributions from any person or entity, NetBird +GmbH ("NetBird") must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license does not change your rights to use your own Contributions for any other purpose. You accept and agree to the following terms and conditions for Your present and future Contributions submitted to -Wiretrustee. Except for the license granted herein to Wiretrustee and recipients of software distributed by Wiretrustee, +NetBird. Except for the license granted herein to NetBird and recipients of software distributed by NetBird, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. ``` "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner - that is making this Agreement with Wiretrustee. For legal entities, the entity making a Contribution and all other + that is making this Agreement with NetBird. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty @@ -91,23 +91,23 @@ You reserve all right, title, and interest in and to Your Contributions. ``` ``` "Contribution" shall mean any original work of authorship, including any modifications or additions to - an existing work, that is or previously has been intentionally submitted by You to Wiretrustee for inclusion in, - or documentation of, any of the products owned or managed by Wiretrustee (the "Work"). + an existing work, that is or previously has been intentionally submitted by You to NetBird for inclusion in, + or documentation of, any of the products owned or managed by NetBird (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication - sent to Wiretrustee or its representatives, including but not limited to communication on electronic mailing lists, + sent to NetBird or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, - Wiretrustee for the purpose of discussing and improving the Work, but excluding communication that is conspicuously + NetBird for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." ``` -2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to Wiretrustee - and to recipients of software distributed by Wiretrustee a perpetual, worldwide, non-exclusive, no-charge, +2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to NetBird + and to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. -3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Wiretrustee and - to recipients of software distributed by Wiretrustee a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to NetBird and + to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which @@ -121,8 +121,8 @@ You reserve all right, title, and interest in and to Your Contributions. intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that you will have received permission from your current and future employers for all future Contributions, that your applicable employer has waived such rights for all of - your current and future Contributions to Wiretrustee, or that your employer has executed a separate Corporate CLA - with Wiretrustee. + your current and future Contributions to NetBird, or that your employer has executed a separate Corporate CLA + with NetBird. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of @@ -138,11 +138,11 @@ You reserve all right, title, and interest in and to Your Contributions. MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. -7. Should You wish to submit work that is not Your original creation, You may submit it to Wiretrustee separately from +7. Should You wish to submit work that is not Your original creation, You may submit it to NetBird separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". -8. You agree to notify Wiretrustee of any facts or circumstances of which you become aware that would make these - representations inaccurate in any respect. \ No newline at end of file +8. You agree to notify NetBird of any facts or circumstances of which you become aware that would make these + representations inaccurate in any respect. diff --git a/LICENSE b/LICENSE index bddb455d5..7cba76dfd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2022 Wiretrustee UG (haftungsbeschränkt) & AUTHORS +Copyright (c) 2022 NetBird GmbH & AUTHORS Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -10,4 +10,4 @@ Redistribution and use in source and binary forms, with or without modification, 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/client/iface/configurer/name.go b/client/iface/configurer/name.go index e2133d0ea..3b9abc0e8 100644 --- a/client/iface/configurer/name.go +++ b/client/iface/configurer/name.go @@ -2,5 +2,5 @@ package configurer -// WgInterfaceDefault is a default interface name of Wiretrustee +// WgInterfaceDefault is a default interface name of Netbird const WgInterfaceDefault = "wt0" diff --git a/client/iface/configurer/name_darwin.go b/client/iface/configurer/name_darwin.go index 034ce388d..eaf04fc2d 100644 --- a/client/iface/configurer/name_darwin.go +++ b/client/iface/configurer/name_darwin.go @@ -2,5 +2,5 @@ package configurer -// WgInterfaceDefault is a default interface name of Wiretrustee +// WgInterfaceDefault is a default interface name of Netbird const WgInterfaceDefault = "utun100" diff --git a/client/iface/device/device_android.go b/client/iface/device/device_android.go index fac2ba63d..772722b83 100644 --- a/client/iface/device/device_android.go +++ b/client/iface/device/device_android.go @@ -63,7 +63,7 @@ func (t *WGTunDevice) Create(routes []string, dns string, searchDomains []string t.filteredDevice = newDeviceFilter(tunDevice) log.Debugf("attaching to interface %v", name) - t.device = device.NewDevice(t.filteredDevice, t.iceBind, device.NewLogger(wgLogLevel(), "[wiretrustee] ")) + t.device = device.NewDevice(t.filteredDevice, t.iceBind, device.NewLogger(wgLogLevel(), "[netbird] ")) // without this property mobile devices can discover remote endpoints if the configured one was wrong. // this helps with support for the older NetBird clients that had a hardcoded direct mode // t.device.DisableSomeRoamingForBrokenMobileSemantics() diff --git a/client/iface/device/device_ios.go b/client/iface/device/device_ios.go index b9591e0b8..cdabd2c85 100644 --- a/client/iface/device/device_ios.go +++ b/client/iface/device/device_ios.go @@ -64,7 +64,7 @@ func (t *TunDevice) Create() (WGConfigurer, error) { t.filteredDevice = newDeviceFilter(tunDevice) log.Debug("Attaching to interface") - t.device = device.NewDevice(t.filteredDevice, t.iceBind, device.NewLogger(wgLogLevel(), "[wiretrustee] ")) + t.device = device.NewDevice(t.filteredDevice, t.iceBind, device.NewLogger(wgLogLevel(), "[netbird] ")) // without this property mobile devices can discover remote endpoints if the configured one was wrong. // this helps with support for the older NetBird clients that had a hardcoded direct mode // t.device.DisableSomeRoamingForBrokenMobileSemantics() diff --git a/client/internal/connect.go b/client/internal/connect.go index 3e3f04f17..ddd10e5cd 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -177,7 +177,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan } }() - // connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config + // connect (just a connection, no stream yet) and login to Management Service to get an initial global Netbird config loginResp, err := loginToManagement(engineCtx, mgmClient, publicSSHKey, c.config) if err != nil { log.Debug(err) @@ -199,8 +199,8 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan c.statusRecorder.UpdateLocalPeerState(localPeerState) signalURL := fmt.Sprintf("%s://%s", - strings.ToLower(loginResp.GetWiretrusteeConfig().GetSignal().GetProtocol().String()), - loginResp.GetWiretrusteeConfig().GetSignal().GetUri(), + strings.ToLower(loginResp.GetNetbirdConfig().GetSignal().GetProtocol().String()), + loginResp.GetNetbirdConfig().GetSignal().GetUri(), ) c.statusRecorder.UpdateSignalAddress(signalURL) @@ -211,8 +211,8 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan c.statusRecorder.MarkSignalDisconnected(err) }() - // with the global Wiretrustee config in hand connect (just a connection, no stream yet) Signal - signalClient, err := connectToSignal(engineCtx, loginResp.GetWiretrusteeConfig(), myPrivateKey) + // with the global Netbird config in hand connect (just a connection, no stream yet) Signal + signalClient, err := connectToSignal(engineCtx, loginResp.GetNetbirdConfig(), myPrivateKey) if err != nil { log.Error(err) return wrapErr(err) @@ -311,7 +311,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan } func parseRelayInfo(loginResp *mgmProto.LoginResponse) ([]string, *hmac.Token) { - relayCfg := loginResp.GetWiretrusteeConfig().GetRelay() + relayCfg := loginResp.GetNetbirdConfig().GetRelay() if relayCfg == nil { return nil, nil } @@ -440,7 +440,7 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe } // connectToSignal creates Signal Service client and established a connection -func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, ourPrivateKey wgtypes.Key) (*signal.GrpcClient, error) { +func connectToSignal(ctx context.Context, wtConfig *mgmProto.NetbirdConfig, ourPrivateKey wgtypes.Key) (*signal.GrpcClient, error) { var sigTLSEnabled bool if wtConfig.Signal.Protocol == mgmProto.HostConfig_HTTPS { sigTLSEnabled = true @@ -457,7 +457,7 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, return signalClient, nil } -// loginToManagement creates Management Services client, establishes a connection, logs-in and gets a global Wiretrustee config (signal, turn, stun hosts, etc) +// loginToManagement creates Management Services client, establishes a connection, logs-in and gets a global Netbird config (signal, turn, stun hosts, etc) func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte, config *Config) (*mgmProto.LoginResponse, error) { serverPublicKey, err := client.GetServerPublicKey() diff --git a/client/internal/engine.go b/client/internal/engine.go index 4f69adfa6..7f7cdf376 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -608,8 +608,8 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() - if update.GetWiretrusteeConfig() != nil { - wCfg := update.GetWiretrusteeConfig() + if update.GetNetbirdConfig() != nil { + wCfg := update.GetNetbirdConfig() err := e.updateTURNs(wCfg.GetTurns()) if err != nil { return fmt.Errorf("update TURNs: %w", err) diff --git a/client/netbird.wxs b/client/netbird.wxs index 0e2be7b3c..ee9ab667f 100644 --- a/client/netbird.wxs +++ b/client/netbird.wxs @@ -1,6 +1,6 @@ - @@ -75,4 +75,4 @@ - \ No newline at end of file + diff --git a/client/proto/daemon.pb.go b/client/proto/daemon.pb.go index 413f94a54..30f7473cd 100644 --- a/client/proto/daemon.pb.go +++ b/client/proto/daemon.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v4.23.4 +// protoc v4.24.3 // source: daemon.proto package proto @@ -92,7 +92,7 @@ type LoginRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // setupKey wiretrustee setup key. + // setupKey netbird setup key. SetupKey string `protobuf:"bytes,1,opt,name=setupKey,proto3" json:"setupKey,omitempty"` // This is the old PreSharedKey field which will be deprecated in favor of optionalPreSharedKey field that is defined as optional // to allow clearing of preshared key while being able to persist in the config file. diff --git a/client/proto/daemon.proto b/client/proto/daemon.proto index b626276de..8db3add08 100644 --- a/client/proto/daemon.proto +++ b/client/proto/daemon.proto @@ -61,7 +61,7 @@ service DaemonService { message LoginRequest { - // setupKey wiretrustee setup key. + // setupKey netbird setup key. string setupKey = 1; // This is the old PreSharedKey field which will be deprecated in favor of optionalPreSharedKey field that is defined as optional diff --git a/client/system/info.go b/client/system/info.go index 4ab4292ae..d83e9509a 100644 --- a/client/system/info.go +++ b/client/system/info.go @@ -50,7 +50,7 @@ type Info struct { OSVersion string Hostname string CPUs int - WiretrusteeVersion string + NetbirdVersion string UIVersion string KernelVersion string NetworkAddresses []NetworkAddress diff --git a/client/system/info_android.go b/client/system/info_android.go index 2d44a6f52..56fe0741d 100644 --- a/client/system/info_android.go +++ b/client/system/info_android.go @@ -36,7 +36,7 @@ func GetInfo(ctx context.Context) *Info { OSVersion: osVersion(), Hostname: extractDeviceName(ctx, "android"), CPUs: runtime.NumCPU(), - WiretrusteeVersion: version.NetbirdVersion(), + NetbirdVersion: version.NetbirdVersion(), UIVersion: extractUIVersion(ctx), KernelVersion: kernelVersion, SystemSerialNumber: serial(), diff --git a/client/system/info_darwin.go b/client/system/info_darwin.go index 13b0a446b..f105ada60 100644 --- a/client/system/info_darwin.go +++ b/client/system/info_darwin.go @@ -63,7 +63,7 @@ func GetInfo(ctx context.Context) *Info { systemHostname, _ := os.Hostname() gio.Hostname = extractDeviceName(ctx, systemHostname) - gio.WiretrusteeVersion = version.NetbirdVersion() + gio.NetbirdVersion = version.NetbirdVersion() gio.UIVersion = extractUserAgent(ctx) return gio diff --git a/client/system/info_freebsd.go b/client/system/info_freebsd.go index 454e58a0b..bed6711de 100644 --- a/client/system/info_freebsd.go +++ b/client/system/info_freebsd.go @@ -39,17 +39,17 @@ func GetInfo(ctx context.Context) *Info { systemHostname, _ := os.Hostname() return &Info{ - GoOS: runtime.GOOS, - Kernel: osInfo[0], - Platform: runtime.GOARCH, - OS: osName, - OSVersion: osVersion, - Hostname: extractDeviceName(ctx, systemHostname), - CPUs: runtime.NumCPU(), - WiretrusteeVersion: version.NetbirdVersion(), - UIVersion: extractUserAgent(ctx), - KernelVersion: osInfo[1], - Environment: env, + GoOS: runtime.GOOS, + Kernel: osInfo[0], + Platform: runtime.GOARCH, + OS: osName, + OSVersion: osVersion, + Hostname: extractDeviceName(ctx, systemHostname), + CPUs: runtime.NumCPU(), + NetbirdVersion: version.NetbirdVersion(), + UIVersion: extractUserAgent(ctx), + KernelVersion: osInfo[1], + Environment: env, } } diff --git a/client/system/info_ios.go b/client/system/info_ios.go index 3dbf50e1e..897ec0a35 100644 --- a/client/system/info_ios.go +++ b/client/system/info_ios.go @@ -19,7 +19,7 @@ func GetInfo(ctx context.Context) *Info { gio := &Info{Kernel: sysName, OSVersion: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: swVersion} gio.Hostname = extractDeviceName(ctx, "hostname") - gio.WiretrusteeVersion = version.NetbirdVersion() + gio.NetbirdVersion = version.NetbirdVersion() gio.UIVersion = extractUserAgent(ctx) return gio diff --git a/client/system/info_linux.go b/client/system/info_linux.go index bfc77be19..9bfc82009 100644 --- a/client/system/info_linux.go +++ b/client/system/info_linux.go @@ -61,7 +61,7 @@ func GetInfo(ctx context.Context) *Info { Hostname: extractDeviceName(ctx, systemHostname), GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), - WiretrusteeVersion: version.NetbirdVersion(), + NetbirdVersion: version.NetbirdVersion(), UIVersion: extractUserAgent(ctx), KernelVersion: osInfo[1], NetworkAddresses: addrs, diff --git a/client/system/info_test.go b/client/system/info_test.go index f44219d9e..27821f3c5 100644 --- a/client/system/info_test.go +++ b/client/system/info_test.go @@ -11,7 +11,7 @@ import ( func Test_LocalWTVersion(t *testing.T) { got := GetInfo(context.TODO()) want := "development" - assert.Equal(t, want, got.WiretrusteeVersion) + assert.Equal(t, want, got.NetbirdVersion) } func Test_UIVersion(t *testing.T) { diff --git a/client/system/info_windows.go b/client/system/info_windows.go index f3f387f28..6f05ded20 100644 --- a/client/system/info_windows.go +++ b/client/system/info_windows.go @@ -64,7 +64,7 @@ func GetInfo(ctx context.Context) *Info { systemHostname, _ := os.Hostname() gio.Hostname = extractDeviceName(ctx, systemHostname) - gio.WiretrusteeVersion = version.NetbirdVersion() + gio.NetbirdVersion = version.NetbirdVersion() gio.UIVersion = extractUserAgent(ctx) return gio diff --git a/management/client/client_test.go b/management/client/client_test.go index 8bd8af8d2..3e498a5ea 100644 --- a/management/client/client_test.go +++ b/management/client/client_test.go @@ -273,8 +273,8 @@ func TestClient_Sync(t *testing.T) { if resp.GetPeerConfig() == nil { t.Error("expecting non nil PeerConfig got nil") } - if resp.GetWiretrusteeConfig() == nil { - t.Error("expecting non nil WiretrusteeConfig got nil") + if resp.GetNetbirdConfig() == nil { + t.Error("expecting non nil NetbirdConfig got nil") } if len(resp.GetRemotePeers()) != 1 { t.Errorf("expecting RemotePeers size %d got %d", 1, len(resp.GetRemotePeers())) @@ -366,15 +366,15 @@ func Test_SystemMetaDataFromClient(t *testing.T) { } expectedMeta := &mgmtProto.PeerSystemMeta{ - Hostname: info.Hostname, - GoOS: info.GoOS, - Kernel: info.Kernel, - Platform: info.Platform, - OS: info.OS, - Core: info.OSVersion, - OSVersion: info.OSVersion, - WiretrusteeVersion: info.WiretrusteeVersion, - KernelVersion: info.KernelVersion, + Hostname: info.Hostname, + GoOS: info.GoOS, + Kernel: info.Kernel, + Platform: info.Platform, + OS: info.OS, + Core: info.OSVersion, + OSVersion: info.OSVersion, + NetbirdVersion: info.NetbirdVersion, + KernelVersion: info.KernelVersion, NetworkAddresses: protoNetAddr, SysSerialNumber: info.SystemSerialNumber, @@ -417,7 +417,7 @@ func isEqual(a, b *mgmtProto.PeerSystemMeta) bool { a.GetPlatform() == b.GetPlatform() && a.GetOS() == b.GetOS() && a.GetOSVersion() == b.GetOSVersion() && - a.GetWiretrusteeVersion() == b.GetWiretrusteeVersion() && + a.GetNetbirdVersion() == b.GetNetbirdVersion() && a.GetUiVersion() == b.GetUiVersion() && a.GetSysSerialNumber() == b.GetSysSerialNumber() && a.GetSysProductName() == b.GetSysProductName() && diff --git a/management/client/grpc.go b/management/client/grpc.go index 9a9c603df..53f66da18 100644 --- a/management/client/grpc.go +++ b/management/client/grpc.go @@ -521,20 +521,20 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta { } return &proto.PeerSystemMeta{ - Hostname: info.Hostname, - GoOS: info.GoOS, - OS: info.OS, - Core: info.OSVersion, - OSVersion: info.OSVersion, - Platform: info.Platform, - Kernel: info.Kernel, - WiretrusteeVersion: info.WiretrusteeVersion, - UiVersion: info.UIVersion, - KernelVersion: info.KernelVersion, - NetworkAddresses: addresses, - SysSerialNumber: info.SystemSerialNumber, - SysManufacturer: info.SystemManufacturer, - SysProductName: info.SystemProductName, + Hostname: info.Hostname, + GoOS: info.GoOS, + OS: info.OS, + Core: info.OSVersion, + OSVersion: info.OSVersion, + Platform: info.Platform, + Kernel: info.Kernel, + NetbirdVersion: info.NetbirdVersion, + UiVersion: info.UIVersion, + KernelVersion: info.KernelVersion, + NetworkAddresses: addresses, + SysSerialNumber: info.SystemSerialNumber, + SysManufacturer: info.SystemManufacturer, + SysProductName: info.SystemProductName, Environment: &proto.Environment{ Cloud: info.Environment.Cloud, Platform: info.Environment.Platform, diff --git a/management/proto/management.pb.go b/management/proto/management.pb.go index ae6559675..a654a6365 100644 --- a/management/proto/management.pb.go +++ b/management/proto/management.pb.go @@ -278,7 +278,7 @@ type EncryptedMessage struct { WgPubKey string `protobuf:"bytes,1,opt,name=wgPubKey,proto3" json:"wgPubKey,omitempty"` // encrypted message Body Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"` - // Version of the Wiretrustee Management Service protocol + // Version of the Netbird Management Service protocol Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` } @@ -383,14 +383,14 @@ func (x *SyncRequest) GetMeta() *PeerSystemMeta { return nil } -// SyncResponse represents a state that should be applied to the local peer (e.g. Wiretrustee servers config as well as local peer and remote peers configs) +// SyncResponse represents a state that should be applied to the local peer (e.g. Netbird servers config as well as local peer and remote peers configs) type SyncResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Global config - WiretrusteeConfig *WiretrusteeConfig `protobuf:"bytes,1,opt,name=wiretrusteeConfig,proto3" json:"wiretrusteeConfig,omitempty"` + NetbirdConfig *NetbirdConfig `protobuf:"bytes,1,opt,name=netbirdConfig,proto3" json:"netbirdConfig,omitempty"` // Deprecated. Use NetworkMap.PeerConfig PeerConfig *PeerConfig `protobuf:"bytes,2,opt,name=peerConfig,proto3" json:"peerConfig,omitempty"` // Deprecated. Use NetworkMap.RemotePeerConfig @@ -435,9 +435,9 @@ func (*SyncResponse) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{2} } -func (x *SyncResponse) GetWiretrusteeConfig() *WiretrusteeConfig { +func (x *SyncResponse) GetNetbirdConfig() *NetbirdConfig { if x != nil { - return x.WiretrusteeConfig + return x.NetbirdConfig } return nil } @@ -885,23 +885,23 @@ type PeerSystemMeta struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` - GoOS string `protobuf:"bytes,2,opt,name=goOS,proto3" json:"goOS,omitempty"` - Kernel string `protobuf:"bytes,3,opt,name=kernel,proto3" json:"kernel,omitempty"` - Core string `protobuf:"bytes,4,opt,name=core,proto3" json:"core,omitempty"` - Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"` - OS string `protobuf:"bytes,6,opt,name=OS,proto3" json:"OS,omitempty"` - WiretrusteeVersion string `protobuf:"bytes,7,opt,name=wiretrusteeVersion,proto3" json:"wiretrusteeVersion,omitempty"` - UiVersion string `protobuf:"bytes,8,opt,name=uiVersion,proto3" json:"uiVersion,omitempty"` - KernelVersion string `protobuf:"bytes,9,opt,name=kernelVersion,proto3" json:"kernelVersion,omitempty"` - OSVersion string `protobuf:"bytes,10,opt,name=OSVersion,proto3" json:"OSVersion,omitempty"` - NetworkAddresses []*NetworkAddress `protobuf:"bytes,11,rep,name=networkAddresses,proto3" json:"networkAddresses,omitempty"` - SysSerialNumber string `protobuf:"bytes,12,opt,name=sysSerialNumber,proto3" json:"sysSerialNumber,omitempty"` - SysProductName string `protobuf:"bytes,13,opt,name=sysProductName,proto3" json:"sysProductName,omitempty"` - SysManufacturer string `protobuf:"bytes,14,opt,name=sysManufacturer,proto3" json:"sysManufacturer,omitempty"` - Environment *Environment `protobuf:"bytes,15,opt,name=environment,proto3" json:"environment,omitempty"` - Files []*File `protobuf:"bytes,16,rep,name=files,proto3" json:"files,omitempty"` - Flags *Flags `protobuf:"bytes,17,opt,name=flags,proto3" json:"flags,omitempty"` + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` + GoOS string `protobuf:"bytes,2,opt,name=goOS,proto3" json:"goOS,omitempty"` + Kernel string `protobuf:"bytes,3,opt,name=kernel,proto3" json:"kernel,omitempty"` + Core string `protobuf:"bytes,4,opt,name=core,proto3" json:"core,omitempty"` + Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"` + OS string `protobuf:"bytes,6,opt,name=OS,proto3" json:"OS,omitempty"` + NetbirdVersion string `protobuf:"bytes,7,opt,name=netbirdVersion,proto3" json:"netbirdVersion,omitempty"` + UiVersion string `protobuf:"bytes,8,opt,name=uiVersion,proto3" json:"uiVersion,omitempty"` + KernelVersion string `protobuf:"bytes,9,opt,name=kernelVersion,proto3" json:"kernelVersion,omitempty"` + OSVersion string `protobuf:"bytes,10,opt,name=OSVersion,proto3" json:"OSVersion,omitempty"` + NetworkAddresses []*NetworkAddress `protobuf:"bytes,11,rep,name=networkAddresses,proto3" json:"networkAddresses,omitempty"` + SysSerialNumber string `protobuf:"bytes,12,opt,name=sysSerialNumber,proto3" json:"sysSerialNumber,omitempty"` + SysProductName string `protobuf:"bytes,13,opt,name=sysProductName,proto3" json:"sysProductName,omitempty"` + SysManufacturer string `protobuf:"bytes,14,opt,name=sysManufacturer,proto3" json:"sysManufacturer,omitempty"` + Environment *Environment `protobuf:"bytes,15,opt,name=environment,proto3" json:"environment,omitempty"` + Files []*File `protobuf:"bytes,16,rep,name=files,proto3" json:"files,omitempty"` + Flags *Flags `protobuf:"bytes,17,opt,name=flags,proto3" json:"flags,omitempty"` } func (x *PeerSystemMeta) Reset() { @@ -978,9 +978,9 @@ func (x *PeerSystemMeta) GetOS() string { return "" } -func (x *PeerSystemMeta) GetWiretrusteeVersion() string { +func (x *PeerSystemMeta) GetNetbirdVersion() string { if x != nil { - return x.WiretrusteeVersion + return x.NetbirdVersion } return "" } @@ -1061,7 +1061,7 @@ type LoginResponse struct { unknownFields protoimpl.UnknownFields // Global config - WiretrusteeConfig *WiretrusteeConfig `protobuf:"bytes,1,opt,name=wiretrusteeConfig,proto3" json:"wiretrusteeConfig,omitempty"` + NetbirdConfig *NetbirdConfig `protobuf:"bytes,1,opt,name=netbirdConfig,proto3" json:"netbirdConfig,omitempty"` // Peer local config PeerConfig *PeerConfig `protobuf:"bytes,2,opt,name=peerConfig,proto3" json:"peerConfig,omitempty"` // Posture checks to be evaluated by client @@ -1100,9 +1100,9 @@ func (*LoginResponse) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{10} } -func (x *LoginResponse) GetWiretrusteeConfig() *WiretrusteeConfig { +func (x *LoginResponse) GetNetbirdConfig() *NetbirdConfig { if x != nil { - return x.WiretrusteeConfig + return x.NetbirdConfig } return nil } @@ -1130,7 +1130,7 @@ type ServerKeyResponse struct { Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // Key expiration timestamp after which the key should be fetched again by the client ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"` - // Version of the Wiretrustee Management Service protocol + // Version of the Netbird Management Service protocol Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` } @@ -1225,8 +1225,8 @@ func (*Empty) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{12} } -// WiretrusteeConfig is a common configuration of any Wiretrustee peer. It contains STUN, TURN, Signal and Management servers configurations -type WiretrusteeConfig struct { +// NetbirdConfig is a common configuration of any Netbird peer. It contains STUN, TURN, Signal and Management servers configurations +type NetbirdConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1240,8 +1240,8 @@ type WiretrusteeConfig struct { Relay *RelayConfig `protobuf:"bytes,4,opt,name=relay,proto3" json:"relay,omitempty"` } -func (x *WiretrusteeConfig) Reset() { - *x = WiretrusteeConfig{} +func (x *NetbirdConfig) Reset() { + *x = NetbirdConfig{} if protoimpl.UnsafeEnabled { mi := &file_management_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1249,13 +1249,13 @@ func (x *WiretrusteeConfig) Reset() { } } -func (x *WiretrusteeConfig) String() string { +func (x *NetbirdConfig) String() string { return protoimpl.X.MessageStringOf(x) } -func (*WiretrusteeConfig) ProtoMessage() {} +func (*NetbirdConfig) ProtoMessage() {} -func (x *WiretrusteeConfig) ProtoReflect() protoreflect.Message { +func (x *NetbirdConfig) ProtoReflect() protoreflect.Message { mi := &file_management_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1267,33 +1267,33 @@ func (x *WiretrusteeConfig) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use WiretrusteeConfig.ProtoReflect.Descriptor instead. -func (*WiretrusteeConfig) Descriptor() ([]byte, []int) { +// Deprecated: Use NetbirdConfig.ProtoReflect.Descriptor instead. +func (*NetbirdConfig) Descriptor() ([]byte, []int) { return file_management_proto_rawDescGZIP(), []int{13} } -func (x *WiretrusteeConfig) GetStuns() []*HostConfig { +func (x *NetbirdConfig) GetStuns() []*HostConfig { if x != nil { return x.Stuns } return nil } -func (x *WiretrusteeConfig) GetTurns() []*ProtectedHostConfig { +func (x *NetbirdConfig) GetTurns() []*ProtectedHostConfig { if x != nil { return x.Turns } return nil } -func (x *WiretrusteeConfig) GetSignal() *HostConfig { +func (x *NetbirdConfig) GetSignal() *HostConfig { if x != nil { return x.Signal } return nil } -func (x *WiretrusteeConfig) GetRelay() *RelayConfig { +func (x *NetbirdConfig) GetRelay() *RelayConfig { if x != nil { return x.Relay } @@ -1306,7 +1306,7 @@ type HostConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // URI of the resource e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000 + // URI of the resource e.g. turns://stun.netbird.io:4430 or signal.netbird.io:10000 Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` Protocol HostConfig_Protocol `protobuf:"varint,2,opt,name=protocol,proto3,enum=management.HostConfig_Protocol" json:"protocol,omitempty"` } @@ -1492,9 +1492,9 @@ type PeerConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Peer's virtual IP address within the Wiretrustee VPN (a Wireguard address config) + // Peer's virtual IP address within the Netbird VPN (a Wireguard address config) Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - // Wiretrustee DNS server (a Wireguard DNS config) + // Netbird DNS server (a Wireguard DNS config) Dns string `protobuf:"bytes,2,opt,name=dns,proto3" json:"dns,omitempty"` // SSHConfig of the peer. SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"` @@ -3067,125 +3067,123 @@ var file_management_proto_rawDesc = []byte{ 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xe7, 0x02, 0x0a, - 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, - 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, - 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, - 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x36, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x52, 0x0a, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x52, 0x06, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x22, 0x41, 0x0a, 0x0f, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, - 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, - 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xa8, 0x01, 0x0a, 0x0c, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, - 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, - 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, - 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x08, 0x70, 0x65, 0x65, 0x72, - 0x4b, 0x65, 0x79, 0x73, 0x22, 0x44, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1a, - 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x3f, 0x0a, 0x0b, 0x45, 0x6e, - 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x12, - 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x5c, 0x0a, 0x04, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x69, 0x73, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x78, 0x69, 0x73, 0x74, 0x12, 0x2a, 0x0a, - 0x10, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x73, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, - 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x49, 0x73, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0xbf, 0x02, 0x0a, 0x05, 0x46, 0x6c, - 0x61, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, - 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, - 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, - 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x53, 0x48, 0x41, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x53, 0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x30, 0x0a, - 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, - 0x30, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x4e, 0x53, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x4e, - 0x53, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x72, 0x65, - 0x77, 0x61, 0x6c, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x22, 0xfa, 0x04, 0x0a, 0x0e, - 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, - 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, - 0x4f, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, 0x4f, 0x53, 0x12, 0x16, - 0x0a, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x2e, 0x0a, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72, - 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x4f, 0x53, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4f, - 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x10, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x79, 0x73, 0x53, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x79, - 0x73, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x73, 0x79, 0x73, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x4d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63, - 0x74, 0x75, 0x72, 0x65, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x79, 0x73, - 0x4d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0b, - 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, - 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x65, 0x6e, 0x76, 0x69, - 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, - 0x27, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6c, 0x61, 0x67, - 0x73, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x69, - 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, + 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xdb, 0x02, 0x0a, + 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, + 0x0d, 0x6e, 0x65, 0x74, 0x62, 0x69, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x62, 0x69, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x0d, 0x6e, 0x65, 0x74, 0x62, 0x69, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, + 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, + 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, + 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x36, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, + 0x61, 0x70, 0x52, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x2a, + 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x73, 0x52, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x22, 0x41, 0x0a, 0x0f, 0x53, 0x79, + 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, + 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xa8, 0x01, + 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, + 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x77, + 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x77, + 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x65, 0x65, 0x72, 0x4b, 0x65, + 0x79, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x08, + 0x70, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x44, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, + 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, + 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x3f, + 0x0a, 0x0b, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, + 0x6f, 0x75, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, + 0x5c, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x78, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x78, 0x69, 0x73, + 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x73, 0x52, 0x75, + 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x49, 0x73, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0xbf, 0x02, + 0x0a, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, + 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x76, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, + 0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x12, 0x30, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x44, 0x4e, 0x53, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x44, 0x4e, 0x53, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x22, + 0xf2, 0x04, 0x0a, 0x0e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, + 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x67, 0x6f, 0x4f, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, + 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, + 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x26, 0x0a, 0x0e, 0x6e, 0x65, + 0x74, 0x62, 0x69, 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x65, 0x74, 0x62, 0x69, 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x24, 0x0a, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4f, 0x53, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x10, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0f, + 0x73, 0x79, 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x79, 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x79, 0x73, 0x50, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x73, 0x79, 0x73, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, + 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x4d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, + 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x79, 0x73, 0x4d, 0x61, 0x6e, 0x75, + 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0b, 0x65, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, + 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x66, + 0x6c, 0x61, 0x67, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x05, 0x66, + 0x6c, 0x61, 0x67, 0x73, 0x22, 0xb4, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x62, 0x69, 0x72, + 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x62, 0x69, + 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x62, 0x69, 0x72, + 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, @@ -3200,308 +3198,308 @@ var file_management_proto_rawDesc = []byte{ 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0xd7, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, - 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, + 0xd3, 0x01, 0x0a, 0x0d, 0x4e, 0x65, 0x74, 0x62, 0x69, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x12, + 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x05, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, + 0x72, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x22, 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, + 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, + 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x48, + 0x54, 0x54, 0x50, 0x53, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, 0x4c, 0x53, 0x10, 0x04, + 0x22, 0x6d, 0x0a, 0x0b, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, + 0x72, 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, + 0x7d, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x05, 0x72, 0x65, - 0x6c, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x05, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, - 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, - 0x4c, 0x53, 0x10, 0x04, 0x22, 0x6d, 0x0a, 0x0b, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x22, 0x7d, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x68, 0x6f, - 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, - 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, - 0x72, 0x64, 0x22, 0xcb, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x33, 0x0a, - 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, - 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x12, 0x48, 0x0a, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, - 0x67, 0x50, 0x65, 0x65, 0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x65, 0x72, 0x44, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x22, 0xf3, 0x04, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, - 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, - 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, - 0x29, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e, - 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x40, 0x0a, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, - 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, - 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, - 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, - 0x6c, 0x65, 0x52, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, - 0x73, 0x12, 0x32, 0x0a, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, - 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x69, 0x67, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, + 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, + 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xcb, + 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, + 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, + 0x64, 0x6e, 0x12, 0x48, 0x0a, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x65, + 0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x52, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x65, 0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xf3, 0x04, 0x0a, + 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, + 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x0c, 0x6f, + 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, + 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, + 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4f, 0x0a, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, - 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, - 0x65, 0x52, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, - 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, - 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, - 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, - 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, - 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, - 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, - 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, - 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, - 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, - 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, - 0x1e, 0x0a, 0x1c, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x5b, 0x0a, 0x15, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, - 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, - 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, - 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, - 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, - 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, - 0x1e, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x34, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x22, 0xed, 0x01, 0x0a, 0x05, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, - 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, - 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, - 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, - 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, - 0x44, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, - 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, - 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, - 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, - 0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, - 0x22, 0x58, 0x0a, 0x0a, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x52, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, - 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, - 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, - 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, - 0x0a, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, - 0x22, 0x8b, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x37, 0x0a, 0x09, 0x44, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x08, - 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x38, - 0x0a, 0x0e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x22, 0x1e, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x72, - 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x1a, - 0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, - 0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0xd1, 0x02, 0x0a, 0x11, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, - 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, - 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, - 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x6f, 0x72, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, - 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x26, 0x0a, - 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2a, 0x4c, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, - 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, - 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, - 0x4d, 0x10, 0x05, 0x2a, 0x20, 0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, - 0x4f, 0x55, 0x54, 0x10, 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, - 0x08, 0x0a, 0x04, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x11, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, - 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, - 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x66, 0x69, 0x72, + 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x4f, 0x0a, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, + 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x13, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, + 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, + 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, + 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, + 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x49, 0x0a, 0x09, + 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, + 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, + 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, + 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, + 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, + 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, + 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, + 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x1e, 0x0a, 0x1c, 0x50, + 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x15, 0x50, + 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, + 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, + 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x55, + 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, + 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x55, 0x52, 0x4c, 0x73, 0x22, 0xed, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, + 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, + 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, + 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, + 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, + 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, + 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x4e, 0x61, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, + 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0a, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb3, 0x01, 0x0a, + 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x4e, + 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x32, + 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x53, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, + 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x8b, 0x02, 0x0a, + 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, + 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x37, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, + 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, + 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, + 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x38, 0x0a, 0x0e, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, + 0x6e, 0x65, 0x74, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, + 0x49, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6d, 0x61, 0x63, 0x22, 0x1e, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, + 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x2f, 0x0a, 0x05, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, + 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd1, 0x02, + 0x0a, 0x11, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, + 0x75, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, + 0x30, 0x0a, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, + 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x12, + 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2a, 0x4c, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, + 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, + 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, + 0x50, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x05, 0x2a, + 0x20, 0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, + 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, + 0x52, 0x4f, 0x50, 0x10, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, + 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, + 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, + 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, + 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, - 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, + 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, + 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3537,7 +3535,7 @@ var file_management_proto_goTypes = []interface{}{ (*LoginResponse)(nil), // 15: management.LoginResponse (*ServerKeyResponse)(nil), // 16: management.ServerKeyResponse (*Empty)(nil), // 17: management.Empty - (*WiretrusteeConfig)(nil), // 18: management.WiretrusteeConfig + (*NetbirdConfig)(nil), // 18: management.NetbirdConfig (*HostConfig)(nil), // 19: management.HostConfig (*RelayConfig)(nil), // 20: management.RelayConfig (*ProtectedHostConfig)(nil), // 21: management.ProtectedHostConfig @@ -3566,7 +3564,7 @@ var file_management_proto_goTypes = []interface{}{ } var file_management_proto_depIdxs = []int32{ 14, // 0: management.SyncRequest.meta:type_name -> management.PeerSystemMeta - 18, // 1: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig + 18, // 1: management.SyncResponse.netbirdConfig:type_name -> management.NetbirdConfig 22, // 2: management.SyncResponse.peerConfig:type_name -> management.PeerConfig 24, // 3: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig 23, // 4: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap @@ -3578,14 +3576,14 @@ var file_management_proto_depIdxs = []int32{ 11, // 10: management.PeerSystemMeta.environment:type_name -> management.Environment 12, // 11: management.PeerSystemMeta.files:type_name -> management.File 13, // 12: management.PeerSystemMeta.flags:type_name -> management.Flags - 18, // 13: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig + 18, // 13: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig 22, // 14: management.LoginResponse.peerConfig:type_name -> management.PeerConfig 39, // 15: management.LoginResponse.Checks:type_name -> management.Checks 43, // 16: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp - 19, // 17: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig - 21, // 18: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig - 19, // 19: management.WiretrusteeConfig.signal:type_name -> management.HostConfig - 20, // 20: management.WiretrusteeConfig.relay:type_name -> management.RelayConfig + 19, // 17: management.NetbirdConfig.stuns:type_name -> management.HostConfig + 21, // 18: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig + 19, // 19: management.NetbirdConfig.signal:type_name -> management.HostConfig + 20, // 20: management.NetbirdConfig.relay:type_name -> management.RelayConfig 3, // 21: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol 19, // 22: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig 25, // 23: management.PeerConfig.sshConfig:type_name -> management.SSHConfig @@ -3796,7 +3794,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WiretrusteeConfig); i { + switch v := v.(*NetbirdConfig); i { case 0: return &v.state case 1: diff --git a/management/proto/management.proto b/management/proto/management.proto index 9db66ec4d..b75d3f956 100644 --- a/management/proto/management.proto +++ b/management/proto/management.proto @@ -52,7 +52,7 @@ message EncryptedMessage { // encrypted message Body bytes body = 2; - // Version of the Wiretrustee Management Service protocol + // Version of the Netbird Management Service protocol int32 version = 3; } @@ -61,11 +61,11 @@ message SyncRequest { PeerSystemMeta meta = 1; } -// SyncResponse represents a state that should be applied to the local peer (e.g. Wiretrustee servers config as well as local peer and remote peers configs) +// SyncResponse represents a state that should be applied to the local peer (e.g. Netbird servers config as well as local peer and remote peers configs) message SyncResponse { // Global config - WiretrusteeConfig wiretrusteeConfig = 1; + NetbirdConfig netbirdConfig = 1; // Deprecated. Use NetworkMap.PeerConfig PeerConfig peerConfig = 2; @@ -146,7 +146,7 @@ message PeerSystemMeta { string core = 4; string platform = 5; string OS = 6; - string wiretrusteeVersion = 7; + string netbirdVersion = 7; string uiVersion = 8; string kernelVersion = 9; string OSVersion = 10; @@ -161,7 +161,7 @@ message PeerSystemMeta { message LoginResponse { // Global config - WiretrusteeConfig wiretrusteeConfig = 1; + NetbirdConfig netbirdConfig = 1; // Peer local config PeerConfig peerConfig = 2; // Posture checks to be evaluated by client @@ -173,14 +173,14 @@ message ServerKeyResponse { string key = 1; // Key expiration timestamp after which the key should be fetched again by the client google.protobuf.Timestamp expiresAt = 2; - // Version of the Wiretrustee Management Service protocol + // Version of the Netbird Management Service protocol int32 version = 3; } message Empty {} -// WiretrusteeConfig is a common configuration of any Wiretrustee peer. It contains STUN, TURN, Signal and Management servers configurations -message WiretrusteeConfig { +// NetbirdConfig is a common configuration of any Netbird peer. It contains STUN, TURN, Signal and Management servers configurations +message NetbirdConfig { // a list of STUN servers repeated HostConfig stuns = 1; // a list of TURN servers @@ -194,7 +194,7 @@ message WiretrusteeConfig { // HostConfig describes connection properties of some server (e.g. STUN, Signal, Management) message HostConfig { - // URI of the resource e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000 + // URI of the resource e.g. turns://stun.netbird.io:4430 or signal.netbird.io:10000 string uri = 1; Protocol protocol = 2; @@ -224,9 +224,9 @@ message ProtectedHostConfig { // PeerConfig represents a configuration of a "our" peer. // The properties are used to configure local Wireguard message PeerConfig { - // Peer's virtual IP address within the Wiretrustee VPN (a Wireguard address config) + // Peer's virtual IP address within the Netbird VPN (a Wireguard address config) string address = 1; - // Wiretrustee DNS server (a Wireguard DNS config) + // Netbird DNS server (a Wireguard DNS config) string dns = 2; // SSHConfig of the peer. diff --git a/management/server/config.go b/management/server/config.go index f3555b92b..397b5f0e6 100644 --- a/management/server/config.go +++ b/management/server/config.go @@ -106,10 +106,10 @@ type HttpServerConfig struct { ExtraAuthAudience string } -// Host represents a Wiretrustee host (e.g. STUN, TURN, Signal) +// Host represents a Netbird host (e.g. STUN, TURN, Signal) type Host struct { Proto Protocol - // URI e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000 + // URI e.g. turns://stun.netbird.io:4430 or signal.netbird.io:10000 URI string Username string Password string diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index a21dcd5b8..eec109ee9 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -381,7 +381,7 @@ func extractPeerMeta(ctx context.Context, meta *proto.PeerSystemMeta) nbpeer.Pee Platform: meta.GetPlatform(), OS: meta.GetOS(), OSVersion: osVersion, - WtVersion: meta.GetWiretrusteeVersion(), + WtVersion: meta.GetNetbirdVersion(), UIVersion: meta.GetUiVersion(), KernelVersion: meta.GetKernelVersion(), NetworkAddresses: networkAddresses, @@ -489,9 +489,9 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p // if peer has reached this point then it has logged in loginResp := &proto.LoginResponse{ - WiretrusteeConfig: toWiretrusteeConfig(s.config, nil, relayToken), - PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain(), false), - Checks: toProtocolChecks(ctx, postureChecks), + NetbirdConfig: toNetbirdConfig(s.config, nil, relayToken), + PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain(), false), + Checks: toProtocolChecks(ctx, postureChecks), } encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp) if err != nil { @@ -547,7 +547,7 @@ func ToResponseProto(configProto Protocol) proto.HostConfig_Protocol { } } -func toWiretrusteeConfig(config *Config, turnCredentials *Token, relayToken *Token) *proto.WiretrusteeConfig { +func toNetbirdConfig(config *Config, turnCredentials *Token, relayToken *Token) *proto.NetbirdConfig { if config == nil { return nil } @@ -595,7 +595,7 @@ func toWiretrusteeConfig(config *Config, turnCredentials *Token, relayToken *Tok } } - return &proto.WiretrusteeConfig{ + return &proto.NetbirdConfig{ Stuns: stuns, Turns: turns, Signal: &proto.HostConfig{ @@ -619,8 +619,8 @@ func toPeerConfig(peer *nbpeer.Peer, network *types.Network, dnsName string, dns func toSyncResponse(ctx context.Context, config *Config, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *DNSConfigCache, dnsResolutionOnRoutingPeerEnbled bool) *proto.SyncResponse { response := &proto.SyncResponse{ - WiretrusteeConfig: toWiretrusteeConfig(config, turnCredentials, relayCredentials), - PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, dnsResolutionOnRoutingPeerEnbled), + NetbirdConfig: toNetbirdConfig(config, turnCredentials, relayCredentials), + PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, dnsResolutionOnRoutingPeerEnbled), NetworkMap: &proto.NetworkMap{ Serial: networkMap.Network.CurrentSerial(), Routes: toProtocolRoutes(networkMap.Routes), diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go index 0df2462f4..bcdf75b8c 100644 --- a/management/server/management_proto_test.go +++ b/management/server/management_proto_test.go @@ -94,7 +94,7 @@ func Test_SyncProtocol(t *testing.T) { mgmtServer, _, mgmtAddr, cleanup, err := startManagementForTest(t, "testdata/store_with_expired_peers.sql", &Config{ Stuns: []*Host{{ Proto: "udp", - URI: "stun:stun.wiretrustee.com:3468", + URI: "stun:stun.netbird.io:3468", }}, TURNConfig: &TURNConfig{ TimeBasedCredentials: false, @@ -102,12 +102,12 @@ func Test_SyncProtocol(t *testing.T) { Secret: "whatever", Turns: []*Host{{ Proto: "udp", - URI: "turn:stun.wiretrustee.com:3468", + URI: "turn:stun.netbird.io:3468", }}, }, Signal: &Host{ Proto: "http", - URI: "signal.wiretrustee.com:10000", + URI: "signal.netbird.io:10000", }, Datadir: dir, HttpConfig: nil, @@ -173,64 +173,64 @@ func Test_SyncProtocol(t *testing.T) { return } - wiretrusteeConfig := syncResp.GetWiretrusteeConfig() - if wiretrusteeConfig == nil { - t.Fatal("expecting SyncResponse to have non-nil WiretrusteeConfig") + netbirdConfig := syncResp.GetNetbirdConfig() + if netbirdConfig == nil { + t.Fatal("expecting SyncResponse to have non-nil NetbirdConfig") } - if wiretrusteeConfig.GetSignal() == nil { - t.Fatal("expecting SyncResponse to have WiretrusteeConfig with non-nil Signal config") + if netbirdConfig.GetSignal() == nil { + t.Fatal("expecting SyncResponse to have NetbirdConfig with non-nil Signal config") } expectedSignalConfig := &mgmtProto.HostConfig{ - Uri: "signal.wiretrustee.com:10000", + Uri: "signal.netbird.io:10000", Protocol: mgmtProto.HostConfig_HTTP, } - if wiretrusteeConfig.GetSignal().GetUri() != expectedSignalConfig.GetUri() { - t.Fatalf("expecting SyncResponse to have WiretrusteeConfig with expected Signal URI: %v, actual: %v", + if netbirdConfig.GetSignal().GetUri() != expectedSignalConfig.GetUri() { + t.Fatalf("expecting SyncResponse to have NetbirdConfig with expected Signal URI: %v, actual: %v", expectedSignalConfig.GetUri(), - wiretrusteeConfig.GetSignal().GetUri()) + netbirdConfig.GetSignal().GetUri()) } - if wiretrusteeConfig.GetSignal().GetProtocol() != expectedSignalConfig.GetProtocol() { - t.Fatalf("expecting SyncResponse to have WiretrusteeConfig with expected Signal Protocol: %v, actual: %v", + if netbirdConfig.GetSignal().GetProtocol() != expectedSignalConfig.GetProtocol() { + t.Fatalf("expecting SyncResponse to have NetbirdConfig with expected Signal Protocol: %v, actual: %v", expectedSignalConfig.GetProtocol().String(), - wiretrusteeConfig.GetSignal().GetProtocol()) + netbirdConfig.GetSignal().GetProtocol()) } expectedStunsConfig := &mgmtProto.HostConfig{ - Uri: "stun:stun.wiretrustee.com:3468", + Uri: "stun:stun.netbird.io:3468", Protocol: mgmtProto.HostConfig_UDP, } - if wiretrusteeConfig.GetStuns()[0].GetUri() != expectedStunsConfig.GetUri() { - t.Fatalf("expecting SyncResponse to have WiretrusteeConfig with expected STUN URI: %v, actual: %v", + if netbirdConfig.GetStuns()[0].GetUri() != expectedStunsConfig.GetUri() { + t.Fatalf("expecting SyncResponse to have NetbirdConfig with expected STUN URI: %v, actual: %v", expectedStunsConfig.GetUri(), - wiretrusteeConfig.GetStuns()[0].GetUri()) + netbirdConfig.GetStuns()[0].GetUri()) } - if wiretrusteeConfig.GetStuns()[0].GetProtocol() != expectedStunsConfig.GetProtocol() { - t.Fatalf("expecting SyncResponse to have WiretrusteeConfig with expected STUN Protocol: %v, actual: %v", + if netbirdConfig.GetStuns()[0].GetProtocol() != expectedStunsConfig.GetProtocol() { + t.Fatalf("expecting SyncResponse to have NetbirdConfig with expected STUN Protocol: %v, actual: %v", expectedStunsConfig.GetProtocol(), - wiretrusteeConfig.GetStuns()[0].GetProtocol()) + netbirdConfig.GetStuns()[0].GetProtocol()) } expectedTRUNHost := &mgmtProto.HostConfig{ - Uri: "turn:stun.wiretrustee.com:3468", + Uri: "turn:stun.netbird.io:3468", Protocol: mgmtProto.HostConfig_UDP, } - if wiretrusteeConfig.GetTurns()[0].GetHostConfig().GetUri() != expectedTRUNHost.GetUri() { - t.Fatalf("expecting SyncResponse to have WiretrusteeConfig with expected TURN URI: %v, actual: %v", + if netbirdConfig.GetTurns()[0].GetHostConfig().GetUri() != expectedTRUNHost.GetUri() { + t.Fatalf("expecting SyncResponse to have NetbirdConfig with expected TURN URI: %v, actual: %v", expectedTRUNHost.GetUri(), - wiretrusteeConfig.GetTurns()[0].GetHostConfig().GetUri()) + netbirdConfig.GetTurns()[0].GetHostConfig().GetUri()) } - if wiretrusteeConfig.GetTurns()[0].GetHostConfig().GetProtocol() != expectedTRUNHost.GetProtocol() { - t.Fatalf("expecting SyncResponse to have WiretrusteeConfig with expected TURN Protocol: %v, actual: %v", + if netbirdConfig.GetTurns()[0].GetHostConfig().GetProtocol() != expectedTRUNHost.GetProtocol() { + t.Fatalf("expecting SyncResponse to have NetbirdConfig with expected TURN Protocol: %v, actual: %v", expectedTRUNHost.GetProtocol().String(), - wiretrusteeConfig.GetTurns()[0].GetHostConfig().GetProtocol()) + netbirdConfig.GetTurns()[0].GetHostConfig().GetProtocol()) } // ensure backward compatibility @@ -285,13 +285,13 @@ func loginPeerWithValidSetupKey(key wgtypes.Key, client mgmtProto.ManagementServ } meta := &mgmtProto.PeerSystemMeta{ - Hostname: key.PublicKey().String(), - GoOS: runtime.GOOS, - OS: runtime.GOOS, - Core: "core", - Platform: "platform", - Kernel: "kernel", - WiretrusteeVersion: "", + Hostname: key.PublicKey().String(), + GoOS: runtime.GOOS, + OS: runtime.GOOS, + Core: "core", + Platform: "platform", + Kernel: "kernel", + NetbirdVersion: "", } message, err := encryption.EncryptMessage(*serverKey, key, &mgmtProto.LoginRequest{SetupKey: TestValidSetupKey, Meta: meta}) if err != nil { @@ -498,7 +498,7 @@ func testSyncStatusRace(t *testing.T) { mgmtServer, am, mgmtAddr, cleanup, err := startManagementForTest(t, "testdata/store_with_expired_peers.sql", &Config{ Stuns: []*Host{{ Proto: "udp", - URI: "stun:stun.wiretrustee.com:3468", + URI: "stun:stun.netbird.io:3468", }}, TURNConfig: &TURNConfig{ TimeBasedCredentials: false, @@ -506,12 +506,12 @@ func testSyncStatusRace(t *testing.T) { Secret: "whatever", Turns: []*Host{{ Proto: "udp", - URI: "turn:stun.wiretrustee.com:3468", + URI: "turn:stun.netbird.io:3468", }}, }, Signal: &Host{ Proto: "http", - URI: "signal.wiretrustee.com:10000", + URI: "signal.netbird.io:10000", }, Datadir: dir, HttpConfig: nil, @@ -670,7 +670,7 @@ func Test_LoginPerformance(t *testing.T) { mgmtServer, am, _, cleanup, err := startManagementForTest(t, "testdata/store_with_expired_peers.sql", &Config{ Stuns: []*Host{{ Proto: "udp", - URI: "stun:stun.wiretrustee.com:3468", + URI: "stun:stun.netbird.io:3468", }}, TURNConfig: &TURNConfig{ TimeBasedCredentials: false, @@ -678,12 +678,12 @@ func Test_LoginPerformance(t *testing.T) { Secret: "whatever", Turns: []*Host{{ Proto: "udp", - URI: "turn:stun.wiretrustee.com:3468", + URI: "turn:stun.netbird.io:3468", }}, }, Signal: &Host{ Proto: "http", - URI: "signal.wiretrustee.com:10000", + URI: "signal.netbird.io:10000", }, Datadir: dir, HttpConfig: nil, @@ -730,13 +730,13 @@ func Test_LoginPerformance(t *testing.T) { } meta := &mgmtProto.PeerSystemMeta{ - Hostname: key.PublicKey().String(), - GoOS: runtime.GOOS, - OS: runtime.GOOS, - Core: "core", - Platform: "platform", - Kernel: "kernel", - WiretrusteeVersion: "", + Hostname: key.PublicKey().String(), + GoOS: runtime.GOOS, + OS: runtime.GOOS, + Core: "core", + Platform: "platform", + Kernel: "kernel", + NetbirdVersion: "", } peerLogin := PeerLogin{ diff --git a/management/server/management_test.go b/management/server/management_test.go index cfa2c138f..43a6e40d5 100644 --- a/management/server/management_test.go +++ b/management/server/management_test.go @@ -47,7 +47,7 @@ var _ = Describe("Management service", func() { level, _ := log.ParseLevel("Debug") log.SetLevel(level) var err error - dataDir, err = os.MkdirTemp("", "wiretrustee_mgmt_test_tmp_*") + dataDir, err = os.MkdirTemp("", "netbird_mgmt_test_tmp_*") Expect(err).NotTo(HaveOccurred()) var listener net.Listener @@ -109,23 +109,23 @@ var _ = Describe("Management service", func() { Expect(err).NotTo(HaveOccurred()) expectedSignalConfig := &mgmtProto.HostConfig{ - Uri: "signal.wiretrustee.com:10000", + Uri: "signal.netbird.io:10000", Protocol: mgmtProto.HostConfig_HTTP, } expectedStunsConfig := &mgmtProto.HostConfig{ - Uri: "stun:stun.wiretrustee.com:3468", + Uri: "stun:stun.netbird.io:3468", Protocol: mgmtProto.HostConfig_UDP, } expectedTRUNHost := &mgmtProto.HostConfig{ - Uri: "turn:stun.wiretrustee.com:3468", + Uri: "turn:stun.netbird.io:3468", Protocol: mgmtProto.HostConfig_UDP, } - Expect(resp.WiretrusteeConfig.Signal).To(BeEquivalentTo(expectedSignalConfig)) - Expect(resp.WiretrusteeConfig.Stuns).To(ConsistOf(expectedStunsConfig)) + Expect(resp.NetbirdConfig.Signal).To(BeEquivalentTo(expectedSignalConfig)) + Expect(resp.NetbirdConfig.Stuns).To(ConsistOf(expectedStunsConfig)) // TURN validation is special because credentials are dynamically generated - Expect(resp.WiretrusteeConfig.Turns).To(HaveLen(1)) - actualTURN := resp.WiretrusteeConfig.Turns[0] + Expect(resp.NetbirdConfig.Turns).To(HaveLen(1)) + actualTURN := resp.NetbirdConfig.Turns[0] Expect(len(actualTURN.User) > 0).To(BeTrue()) Expect(actualTURN.HostConfig).To(BeEquivalentTo(expectedTRUNHost)) Expect(len(resp.NetworkMap.OfflinePeers) == 0).To(BeTrue()) @@ -286,25 +286,25 @@ var _ = Describe("Management service", func() { Expect(err).NotTo(HaveOccurred()) expectedSignalConfig := &mgmtProto.HostConfig{ - Uri: "signal.wiretrustee.com:10000", + Uri: "signal.netbird.io:10000", Protocol: mgmtProto.HostConfig_HTTP, } expectedStunsConfig := &mgmtProto.HostConfig{ - Uri: "stun:stun.wiretrustee.com:3468", + Uri: "stun:stun.netbird.io:3468", Protocol: mgmtProto.HostConfig_UDP, } expectedTurnsConfig := &mgmtProto.ProtectedHostConfig{ HostConfig: &mgmtProto.HostConfig{ - Uri: "turn:stun.wiretrustee.com:3468", + Uri: "turn:stun.netbird.io:3468", Protocol: mgmtProto.HostConfig_UDP, }, User: "some_user", Password: "some_password", } - Expect(decryptedResp.GetWiretrusteeConfig().Signal).To(BeEquivalentTo(expectedSignalConfig)) - Expect(decryptedResp.GetWiretrusteeConfig().Stuns).To(ConsistOf(expectedStunsConfig)) - Expect(decryptedResp.GetWiretrusteeConfig().Turns).To(ConsistOf(expectedTurnsConfig)) + Expect(decryptedResp.GetNetbirdConfig().Signal).To(BeEquivalentTo(expectedSignalConfig)) + Expect(decryptedResp.GetNetbirdConfig().Stuns).To(ConsistOf(expectedStunsConfig)) + Expect(decryptedResp.GetNetbirdConfig().Turns).To(ConsistOf(expectedTurnsConfig)) }) }) }) @@ -449,13 +449,13 @@ func loginPeerWithValidSetupKey(serverPubKey wgtypes.Key, key wgtypes.Key, clien defer GinkgoRecover() meta := &mgmtProto.PeerSystemMeta{ - Hostname: key.PublicKey().String(), - GoOS: runtime.GOOS, - OS: runtime.GOOS, - Core: "core", - Platform: "platform", - Kernel: "kernel", - WiretrusteeVersion: "", + Hostname: key.PublicKey().String(), + GoOS: runtime.GOOS, + OS: runtime.GOOS, + Core: "core", + Platform: "platform", + Kernel: "kernel", + NetbirdVersion: "", } message, err := encryption.EncryptMessage(serverPubKey, key, &mgmtProto.LoginRequest{SetupKey: ValidSetupKey, Meta: meta}) Expect(err).NotTo(HaveOccurred()) diff --git a/management/server/peer_test.go b/management/server/peer_test.go index 40f8d15d5..a0417c996 100644 --- a/management/server/peer_test.go +++ b/management/server/peer_test.go @@ -1099,13 +1099,13 @@ func TestToSyncResponse(t *testing.T) { assert.Equal(t, "192.168.1.1/24", response.PeerConfig.Address) assert.Equal(t, "peer1.example.com", response.PeerConfig.Fqdn) assert.Equal(t, true, response.PeerConfig.SshConfig.SshEnabled) - // assert wiretrustee config - assert.Equal(t, "signal.uri", response.WiretrusteeConfig.Signal.Uri) - assert.Equal(t, proto.HostConfig_HTTPS, response.WiretrusteeConfig.Signal.GetProtocol()) - assert.Equal(t, "stun.uri", response.WiretrusteeConfig.Stuns[0].Uri) - assert.Equal(t, "turn.uri", response.WiretrusteeConfig.Turns[0].HostConfig.GetUri()) - assert.Equal(t, "turn-user", response.WiretrusteeConfig.Turns[0].User) - assert.Equal(t, "turn-pass", response.WiretrusteeConfig.Turns[0].Password) + // assert netbird config + assert.Equal(t, "signal.uri", response.NetbirdConfig.Signal.Uri) + assert.Equal(t, proto.HostConfig_HTTPS, response.NetbirdConfig.Signal.GetProtocol()) + assert.Equal(t, "stun.uri", response.NetbirdConfig.Stuns[0].Uri) + assert.Equal(t, "turn.uri", response.NetbirdConfig.Turns[0].HostConfig.GetUri()) + assert.Equal(t, "turn-user", response.NetbirdConfig.Turns[0].User) + assert.Equal(t, "turn-pass", response.NetbirdConfig.Turns[0].Password) // assert RemotePeers assert.Equal(t, 1, len(response.RemotePeers)) assert.Equal(t, "192.168.1.2/32", response.RemotePeers[0].AllowedIps[0]) diff --git a/management/server/testdata/management.json b/management/server/testdata/management.json index d29491118..f797a7d2b 100644 --- a/management/server/testdata/management.json +++ b/management/server/testdata/management.json @@ -2,7 +2,7 @@ "Stuns": [ { "Proto": "udp", - "URI": "stun:stun.wiretrustee.com:3468", + "URI": "stun:stun.netbird.io:3468", "Username": "", "Password": null } @@ -11,7 +11,7 @@ "Turns": [ { "Proto": "udp", - "URI": "turn:stun.wiretrustee.com:3468", + "URI": "turn:stun.netbird.io:3468", "Username": "some_user", "Password": "some_password" } @@ -22,7 +22,7 @@ }, "Signal": { "Proto": "http", - "URI": "signal.wiretrustee.com:10000", + "URI": "signal.netbird.io:10000", "Username": "", "Password": null }, @@ -44,4 +44,4 @@ "GrantType": "client_credentials" } } -} \ No newline at end of file +} diff --git a/management/server/token_mgr.go b/management/server/token_mgr.go index fd67fa3e3..ec8aae47e 100644 --- a/management/server/token_mgr.go +++ b/management/server/token_mgr.go @@ -199,7 +199,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Cont } update := &proto.SyncResponse{ - WiretrusteeConfig: &proto.WiretrusteeConfig{ + NetbirdConfig: &proto.NetbirdConfig{ Turns: turns, }, } @@ -208,7 +208,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Cont if m.relayCfg != nil { token, err := m.GenerateRelayToken() if err == nil { - update.WiretrusteeConfig.Relay = &proto.RelayConfig{ + update.NetbirdConfig.Relay = &proto.RelayConfig{ Urls: m.relayCfg.Addresses, TokenPayload: token.Payload, TokenSignature: token.Signature, @@ -228,7 +228,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, pe } update := &proto.SyncResponse{ - WiretrusteeConfig: &proto.WiretrusteeConfig{ + NetbirdConfig: &proto.NetbirdConfig{ Relay: &proto.RelayConfig{ Urls: m.relayCfg.Addresses, TokenPayload: string(relayToken.Payload), diff --git a/management/server/token_mgr_test.go b/management/server/token_mgr_test.go index 2aafb9f68..f2b056d8f 100644 --- a/management/server/token_mgr_test.go +++ b/management/server/token_mgr_test.go @@ -18,7 +18,7 @@ import ( var TurnTestHost = &Host{ Proto: UDP, - URI: "turn:turn.wiretrustee.com:77777", + URI: "turn:turn.netbird.io:77777", Username: "username", Password: "", } @@ -124,7 +124,7 @@ loop: var firstRelayUpdate, secondRelayUpdate *proto.RelayConfig for _, update := range updates { - if turns := update.Update.GetWiretrusteeConfig().GetTurns(); len(turns) > 0 { + if turns := update.Update.GetNetbirdConfig().GetTurns(); len(turns) > 0 { turnUpdates++ if turnUpdates == 1 { firstTurnUpdate = turns[0] @@ -132,9 +132,9 @@ loop: secondTurnUpdate = turns[0] } } - if relay := update.Update.GetWiretrusteeConfig().GetRelay(); relay != nil { + if relay := update.Update.GetNetbirdConfig().GetRelay(); relay != nil { // avoid updating on turn updates since they also send relay credentials - if update.Update.GetWiretrusteeConfig().GetTurns() == nil { + if update.Update.GetNetbirdConfig().GetTurns() == nil { relayUpdates++ if relayUpdates == 1 { firstRelayUpdate = relay diff --git a/release_files/darwin-ui-installer.sh b/release_files/darwin-ui-installer.sh index 5179f02d6..de331730f 100644 --- a/release_files/darwin-ui-installer.sh +++ b/release_files/darwin-ui-installer.sh @@ -2,13 +2,13 @@ export PATH=$PATH:/usr/local/bin:/opt/homebrew/bin -# check if wiretrustee is installed -WT_BIN=$(which wiretrustee) -if [ -n "$WT_BIN" ] +# check if netbird is installed +NB_BIN=$(which netbird) +if [ -n "$NB_BIN" ] then - echo "Stopping and uninstalling Wiretrustee daemon" - wiretrustee service stop || true - wiretrustee service uninstall || true + echo "Stopping and uninstalling Netbird daemon" + netbird service stop || true + netbird service uninstall || true fi # check if netbird is installed diff --git a/release_files/install.sh b/release_files/install.sh index bb917c39a..459645c58 100755 --- a/release_files/install.sh +++ b/release_files/install.sh @@ -263,16 +263,16 @@ install_netbird() { add_aur_repo ;; brew) - # Remove Wiretrustee if it had been installed using Homebrew before - if brew ls --versions wiretrustee >/dev/null 2>&1; then - echo "Removing existing wiretrustee client" + # Remove Netbird if it had been installed using Homebrew before + if brew ls --versions netbird >/dev/null 2>&1; then + echo "Removing existing netbird client" # Stop and uninstall daemon service: - wiretrustee service stop - wiretrustee service uninstall + netbird service stop + netbird service uninstall # Unlik the app - brew unlink wiretrustee + brew unlink netbird fi brew install netbirdio/tap/netbird diff --git a/signal/README.md b/signal/README.md index 9e3207cfa..0033eaf90 100644 --- a/signal/README.md +++ b/signal/README.md @@ -60,7 +60,7 @@ subdomain sub.mydomain.com). ```bash # create a volume -docker volume create wiretrustee-signal +docker volume create netbird-signal # run the docker container docker run -d --name netbird-signal \ -p 10000:10000 \ From 125b5e2b163a5c3bd1ac3ca5d99a5c04972748fb Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:55:42 +0100 Subject: [PATCH 5/9] [client] Fix acl empty port range detection (#3285) --- client/internal/acl/manager.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/client/internal/acl/manager.go b/client/internal/acl/manager.go index 9ec0bb031..a015e0a49 100644 --- a/client/internal/acl/manager.go +++ b/client/internal/acl/manager.go @@ -268,7 +268,7 @@ func (d *DefaultManager) protoRuleToFirewallRule( } var port *firewall.Port - if r.PortInfo != nil { + if !portInfoEmpty(r.PortInfo) { port = convertPortInfo(r.PortInfo) } else if r.Port != "" { // old version of management, single port @@ -305,6 +305,22 @@ func (d *DefaultManager) protoRuleToFirewallRule( return ruleID, rules, nil } +func portInfoEmpty(portInfo *mgmProto.PortInfo) bool { + if portInfo == nil { + return true + } + + switch portInfo.GetPortSelection().(type) { + case *mgmProto.PortInfo_Port: + return portInfo.GetPort() == 0 + case *mgmProto.PortInfo_Range_: + r := portInfo.GetRange() + return r == nil || r.Start == 0 || r.End == 0 + default: + return true + } +} + func (d *DefaultManager) addInRules( ip net.IP, protocol firewall.Protocol, From fe370e7d8f6feef4bd6aecb584c514eb09b1b679 Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Wed, 5 Feb 2025 14:03:53 -0800 Subject: [PATCH 6/9] [relay] Use new upstream for nhooyr.io/websocket package (#3287) The nhooyr.io/websocket package was renamed to github.com/coder/websocket when the project was transferred to "coder" as the new maintainer. Use the new import path and update go.mod and go.sum accordingly. Signed-off-by: Christian Stewart --- go.mod | 2 +- go.sum | 4 ++-- relay/client/dialer/ws/conn.go | 2 +- relay/client/dialer/ws/ws.go | 2 +- relay/server/listener/ws/conn.go | 2 +- relay/server/listener/ws/listener.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 13adeff09..77d570662 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/c-robinson/iplib v1.0.3 github.com/caddyserver/certmagic v0.21.3 github.com/cilium/ebpf v0.15.0 + github.com/coder/websocket v1.8.12 github.com/coreos/go-iptables v0.7.0 github.com/creack/pty v1.1.18 github.com/davecgh/go-spew v1.1.1 @@ -101,7 +102,6 @@ require ( gorm.io/driver/postgres v1.5.7 gorm.io/driver/sqlite v1.5.7 gorm.io/gorm v1.25.12 - nhooyr.io/websocket v1.8.11 ) require ( diff --git a/go.sum b/go.sum index e3670b99e..4b9e90eba 100644 --- a/go.sum +++ b/go.sum @@ -137,6 +137,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= +github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/containerd v1.7.16 h1:7Zsfe8Fkj4Wi2My6DXGQ87hiqIrmOXolm72ZEkFU5Mg= github.com/containerd/containerd v1.7.16/go.mod h1:NL49g7A/Fui7ccmxV6zkBWwqMgmMxFWzujYCc+JLt7k= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -1264,8 +1266,6 @@ k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= -nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/relay/client/dialer/ws/conn.go b/relay/client/dialer/ws/conn.go index 74bcafd82..0086b702b 100644 --- a/relay/client/dialer/ws/conn.go +++ b/relay/client/dialer/ws/conn.go @@ -6,7 +6,7 @@ import ( "net" "time" - "nhooyr.io/websocket" + "github.com/coder/websocket" ) type Conn struct { diff --git a/relay/client/dialer/ws/ws.go b/relay/client/dialer/ws/ws.go index 2adbd2451..b007e24bb 100644 --- a/relay/client/dialer/ws/ws.go +++ b/relay/client/dialer/ws/ws.go @@ -12,7 +12,7 @@ import ( "strings" log "github.com/sirupsen/logrus" - "nhooyr.io/websocket" + "github.com/coder/websocket" "github.com/netbirdio/netbird/relay/server/listener/ws" "github.com/netbirdio/netbird/util/embeddedroots" diff --git a/relay/server/listener/ws/conn.go b/relay/server/listener/ws/conn.go index 12e721fdb..3466b2abd 100644 --- a/relay/server/listener/ws/conn.go +++ b/relay/server/listener/ws/conn.go @@ -9,7 +9,7 @@ import ( "time" log "github.com/sirupsen/logrus" - "nhooyr.io/websocket" + "github.com/coder/websocket" ) const ( diff --git a/relay/server/listener/ws/listener.go b/relay/server/listener/ws/listener.go index 0eb244c77..4597669dc 100644 --- a/relay/server/listener/ws/listener.go +++ b/relay/server/listener/ws/listener.go @@ -9,7 +9,7 @@ import ( "net/http" log "github.com/sirupsen/logrus" - "nhooyr.io/websocket" + "github.com/coder/websocket" ) // URLPath is the path for the websocket connection. From e00a280329f77e9572ec43b0005366916b9bcce7 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Wed, 5 Feb 2025 23:04:52 +0100 Subject: [PATCH 7/9] [client] Fix grouping of peer ACLs with different port ranges (#3289) --- client/internal/acl/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/internal/acl/manager.go b/client/internal/acl/manager.go index a015e0a49..31173a5f7 100644 --- a/client/internal/acl/manager.go +++ b/client/internal/acl/manager.go @@ -507,7 +507,7 @@ func (d *DefaultManager) squashAcceptRules( // getRuleGroupingSelector takes all rule properties except IP address to build selector func (d *DefaultManager) getRuleGroupingSelector(rule *mgmProto.FirewallRule) string { - return fmt.Sprintf("%v:%v:%v:%s", strconv.Itoa(int(rule.Direction)), rule.Action, rule.Protocol, rule.Port) + return fmt.Sprintf("%v:%v:%v:%s:%v", strconv.Itoa(int(rule.Direction)), rule.Action, rule.Protocol, rule.Port, rule.PortInfo) } func (d *DefaultManager) rollBack(newRulePairs map[id.RuleID][]firewall.Rule) { From ca9aca9b19b82d2976099c92d9ec7d9de8eb44c4 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Thu, 6 Feb 2025 10:20:31 +0100 Subject: [PATCH 8/9] Fix nil pointer exception when load empty list and try to cast it (#3282) --- client/internal/engine.go | 3 +-- client/internal/peer/ice/StunTurn.go | 22 ++++++++++++++++++++++ client/internal/peer/ice/StunTurn_test.go | 13 +++++++++++++ client/internal/peer/ice/agent.go | 3 +-- client/internal/peer/ice/config.go | 4 +--- 5 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 client/internal/peer/ice/StunTurn.go create mode 100644 client/internal/peer/ice/StunTurn_test.go diff --git a/client/internal/engine.go b/client/internal/engine.go index 7f7cdf376..335729d92 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -13,7 +13,6 @@ import ( "sort" "strings" "sync" - "sync/atomic" "time" "github.com/hashicorp/go-multierror" @@ -146,7 +145,7 @@ type Engine struct { STUNs []*stun.URI // TURNs is a list of STUN servers used by ICE TURNs []*stun.URI - stunTurn atomic.Value + stunTurn icemaker.StunTurn clientCtx context.Context clientCancel context.CancelFunc diff --git a/client/internal/peer/ice/StunTurn.go b/client/internal/peer/ice/StunTurn.go new file mode 100644 index 000000000..63ee8c713 --- /dev/null +++ b/client/internal/peer/ice/StunTurn.go @@ -0,0 +1,22 @@ +package ice + +import ( + "sync/atomic" + + "github.com/pion/stun/v2" +) + +type StunTurn atomic.Value + +func (s *StunTurn) Load() []*stun.URI { + v := (*atomic.Value)(s).Load() + if v == nil { + return nil + } + + return v.([]*stun.URI) +} + +func (s *StunTurn) Store(value []*stun.URI) { + (*atomic.Value)(s).Store(value) +} diff --git a/client/internal/peer/ice/StunTurn_test.go b/client/internal/peer/ice/StunTurn_test.go new file mode 100644 index 000000000..7233afa6c --- /dev/null +++ b/client/internal/peer/ice/StunTurn_test.go @@ -0,0 +1,13 @@ +package ice + +import ( + "testing" +) + +func TestStunTurn_LoadEmpty(t *testing.T) { + var stStunTurn StunTurn + got := stStunTurn.Load() + if len(got) != 0 { + t.Errorf("StunTurn.Load() = %v, want %v", got, nil) + } +} diff --git a/client/internal/peer/ice/agent.go b/client/internal/peer/ice/agent.go index dc4750f24..af9e60f2d 100644 --- a/client/internal/peer/ice/agent.go +++ b/client/internal/peer/ice/agent.go @@ -5,7 +5,6 @@ import ( "github.com/pion/ice/v3" "github.com/pion/randutil" - "github.com/pion/stun/v2" log "github.com/sirupsen/logrus" "github.com/netbirdio/netbird/client/internal/stdnet" @@ -39,7 +38,7 @@ func NewAgent(iFaceDiscover stdnet.ExternalIFaceDiscover, config Config, candida agentConfig := &ice.AgentConfig{ MulticastDNSMode: ice.MulticastDNSModeDisabled, NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4, ice.NetworkTypeUDP6}, - Urls: config.StunTurn.Load().([]*stun.URI), + Urls: config.StunTurn.Load(), CandidateTypes: candidateTypes, InterfaceFilter: stdnet.InterfaceFilter(config.InterfaceBlackList), UDPMux: config.UDPMux, diff --git a/client/internal/peer/ice/config.go b/client/internal/peer/ice/config.go index 8abc842f0..dd854a605 100644 --- a/client/internal/peer/ice/config.go +++ b/client/internal/peer/ice/config.go @@ -1,14 +1,12 @@ package ice import ( - "sync/atomic" - "github.com/pion/ice/v3" ) type Config struct { // StunTurn is a list of STUN and TURN URLs - StunTurn *atomic.Value // []*stun.URI + StunTurn *StunTurn // []*stun.URI // InterfaceBlackList is a list of machine interfaces that should be filtered out by ICE Candidate gathering // (e.g. if eth0 is in the list, host candidate of this interface won't be used) From cee4aeea9e48376707974f301f280b3d813131a9 Mon Sep 17 00:00:00 2001 From: Pascal Fischer <32096965+pascal-fischer@users.noreply.github.com> Date: Thu, 6 Feb 2025 13:36:57 +0100 Subject: [PATCH 9/9] [management] Check groups when counting peers on networks list (#3284) --- management/server/http/handlers/networks/handler.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/management/server/http/handlers/networks/handler.go b/management/server/http/handlers/networks/handler.go index 316b93611..f716348d6 100644 --- a/management/server/http/handlers/networks/handler.go +++ b/management/server/http/handlers/networks/handler.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/gorilla/mux" + log "github.com/sirupsen/logrus" s "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/groups" @@ -281,7 +282,12 @@ func (h *handler) collectIDsInNetwork(ctx context.Context, accountID, userID, ne } if len(router.PeerGroups) > 0 { for _, groupID := range router.PeerGroups { - peerCounter += len(groups[groupID].Peers) + group, ok := groups[groupID] + if !ok { + log.WithContext(ctx).Warnf("group %s not found", groupID) + continue + } + peerCounter += len(group.Peers) } } }