Merge remote-tracking branch 'origin/main' into fix_debug_upload_url_from_mgmt

# Conflicts:
#	client/proto/daemon.pb.go
#	management/internals/controllers/network_map/controller/controller.go
#	management/internals/controllers/network_map/nmaptest/runner.go
#	management/internals/shared/grpc/server.go
#	management/server/account.go
#	shared/management/proto/management.pb.go
#	upload-server/server/server.go
This commit is contained in:
riccardom
2026-10-09 11:13:57 +02:00
518 changed files with 30626 additions and 12765 deletions
+5 -1
View File
@@ -21,7 +21,11 @@ type Client interface {
// is not eligible for session extension.
ExtendAuthSession(sysInfo *system.Info, jwtToken string) (*proto.ExtendAuthSessionResponse, error)
GetDeviceAuthorizationFlow() (*proto.DeviceAuthorizationFlow, error)
GetPKCEAuthorizationFlow() (*proto.PKCEAuthorizationFlow, error)
// GetPKCEAuthorizationFlow returns the PKCE authorization flow information.
//
// sessionExtend marks the flow as renewing an existing peer's session rather
// than logging one in. See PKCEAuthorizationFlowRequest.
GetPKCEAuthorizationFlow(sessionExtend bool) (*proto.PKCEAuthorizationFlow, error)
GetServerURL() string
// IsHealthy returns the current connection status without blocking.
// Used by the engine to monitor connectivity in the background.
+12 -8
View File
@@ -10,10 +10,10 @@ import (
"testing"
"time"
"go.uber.org/mock/gomock"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@@ -35,7 +35,6 @@ import (
"github.com/netbirdio/netbird/management/server/activity"
nbcache "github.com/netbirdio/netbird/management/server/cache"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/mock_server"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
@@ -128,8 +127,8 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
updateManager := update_channel.NewPeersUpdateManager(metrics)
requestBuffer := mgmt.NewAccountRequestBuffer(ctx, store)
networkMapController := controller.NewController(ctx, store, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), ephemeral_manager.NewEphemeralManager(store, peersManger), config, nil)
accountManager, err := mgmt.BuildManager(context.Background(), config, store, networkMapController, jobManager, nil, "", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false, cacheStore)
networkMapController := controller.NewController(ctx, store, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", ephemeral_manager.NewEphemeralManager(store, peersManger), config, nil)
accountManager, err := mgmt.BuildManager(context.Background(), config, store, networkMapController, jobManager, nil, "", eventStore, nil, false, ia, metrics, settingsMockManager, permissionsManagerMock, false, cacheStore)
if err != nil {
t.Fatal(err)
}
@@ -666,7 +665,12 @@ func Test_GetPKCEAuthorizationFlow(t *testing.T) {
},
}
var gotRequest mgmtProto.PKCEAuthorizationFlowRequest
mgmtMockServer.GetPKCEAuthorizationFlowFunc = func(ctx context.Context, req *mgmtProto.EncryptedMessage) (*mgmtProto.EncryptedMessage, error) {
if err := encryption.DecryptMessage(client.key.PublicKey(), serverKey, req.Body, &gotRequest); err != nil {
return nil, err
}
encryptedResp, err := encryption.EncryptMessage(client.key.PublicKey(), serverKey, expectedFlowInfo)
if err != nil {
return nil, err
@@ -679,10 +683,10 @@ func Test_GetPKCEAuthorizationFlow(t *testing.T) {
}, nil
}
flowInfo, err := client.GetPKCEAuthorizationFlow()
if err != nil {
t.Error("error while retrieving pkce auth flow information")
}
flowInfo, err := client.GetPKCEAuthorizationFlow(true)
require.NoError(t, err)
assert.True(t, gotRequest.GetSessionExtend(), "session extend should reach the server")
assert.Equal(t, expectedFlowInfo.ProviderConfig.ClientID, flowInfo.ProviderConfig.ClientID, "provider configured client ID should match")
assert.Equal(t, expectedFlowInfo.ProviderConfig.ClientSecret, flowInfo.ProviderConfig.ClientSecret, "provider configured client secret should match") //nolint:staticcheck
+7 -3
View File
@@ -738,9 +738,13 @@ func (c *GrpcClient) GetDeviceAuthorizationFlow() (*proto.DeviceAuthorizationFlo
return flowInfoResp, nil
}
// GetPKCEAuthorizationFlow returns a pkce authorization flow information.
// GetPKCEAuthorizationFlow returns the PKCE authorization flow information.
// It also takes care of encrypting and decrypting messages.
func (c *GrpcClient) GetPKCEAuthorizationFlow() (*proto.PKCEAuthorizationFlow, error) {
//
// sessionExtend tells the server the flow will renew an existing peer's session
// rather than log one in, so it can rule out a configuration that would let the
// IdP answer from an unrelated account. See PKCEAuthorizationFlowRequest.
func (c *GrpcClient) GetPKCEAuthorizationFlow(sessionExtend bool) (*proto.PKCEAuthorizationFlow, error) {
if !c.ready() {
return nil, fmt.Errorf("no connection to management in order to get pkce authorization flow")
}
@@ -753,7 +757,7 @@ func (c *GrpcClient) GetPKCEAuthorizationFlow() (*proto.PKCEAuthorizationFlow, e
mgmCtx, cancel := context.WithTimeout(c.ctx, time.Second*2)
defer cancel()
message := &proto.PKCEAuthorizationFlowRequest{}
message := &proto.PKCEAuthorizationFlowRequest{SessionExtend: sessionExtend}
encryptedMSG, err := encryption.EncryptMessage(*serverKey, c.key, message)
if err != nil {
return nil, err
+4 -3
View File
@@ -16,7 +16,7 @@ type MockClient struct {
LoginFunc func(info *system.Info, sshKey []byte, dnsLabels domain.List) (*proto.LoginResponse, error)
ExtendAuthSessionFunc func(info *system.Info, jwtToken string) (*proto.ExtendAuthSessionResponse, error)
GetDeviceAuthorizationFlowFunc func() (*proto.DeviceAuthorizationFlow, error)
GetPKCEAuthorizationFlowFunc func() (*proto.PKCEAuthorizationFlow, error)
GetPKCEAuthorizationFlowFunc func(sessionExtend bool) (*proto.PKCEAuthorizationFlow, error)
GetServerURLFunc func() string
HealthCheckFunc func() error
SyncMetaFunc func(sysInfo *system.Info) error
@@ -80,11 +80,12 @@ func (m *MockClient) GetDeviceAuthorizationFlow() (*proto.DeviceAuthorizationFlo
return m.GetDeviceAuthorizationFlowFunc()
}
func (m *MockClient) GetPKCEAuthorizationFlow() (*proto.PKCEAuthorizationFlow, error) {
// GetPKCEAuthorizationFlow returns the PKCE authorization flow information.
func (m *MockClient) GetPKCEAuthorizationFlow(sessionExtend bool) (*proto.PKCEAuthorizationFlow, error) {
if m.GetPKCEAuthorizationFlowFunc == nil {
return nil, nil
}
return m.GetPKCEAuthorizationFlowFunc()
return m.GetPKCEAuthorizationFlowFunc(sessionExtend)
}
func (m *MockClient) HealthCheck() error {
-5
View File
@@ -127,10 +127,6 @@ type Client struct {
// see more: https://docs.netbird.io/api/resources/identity-providers
IdentityProviders *IdentityProvidersAPI
// Ingress NetBird Ingress Peers APIs
// see more: https://docs.netbird.io/api/resources/ingress-ports
Ingress *IngressAPI
// Instance NetBird Instance API
// see more: https://docs.netbird.io/api/resources/instance
Instance *InstanceAPI
@@ -207,7 +203,6 @@ func (c *Client) initialize() {
c.OktaScimIDP = &OktaScimIDPAPI{c}
c.EventStreaming = &EventStreamingAPI{c}
c.IdentityProviders = &IdentityProvidersAPI{c}
c.Ingress = &IngressAPI{c}
c.Instance = &InstanceAPI{c}
c.ReverseProxyServices = &ReverseProxyServicesAPI{c}
c.ReverseProxyClusters = &ReverseProxyClustersAPI{c}
-92
View File
@@ -1,92 +0,0 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// IngressAPI APIs for Ingress Peers, do not use directly
type IngressAPI struct {
c *Client
}
// List all ingress peers
// See more: https://docs.netbird.io/api/resources/ingress#list-all-ingress-peers
func (a *IngressAPI) List(ctx context.Context) ([]api.IngressPeer, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/ingress/peers", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IngressPeer](resp)
return ret, err
}
// Get ingress peer info
// See more: https://docs.netbird.io/api/resources/ingress#retrieve-an-ingress-peer
func (a *IngressAPI) Get(ctx context.Context, ingressPeerID string) (*api.IngressPeer, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/ingress/peers/"+ingressPeerID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPeer](resp)
return &ret, err
}
// Create new ingress peer
// See more: https://docs.netbird.io/api/resources/ingress#create-an-ingress-peer
func (a *IngressAPI) Create(ctx context.Context, request api.PostApiIngressPeersJSONRequestBody) (*api.IngressPeer, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/ingress/peers", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPeer](resp)
return &ret, err
}
// Update update ingress peer
// See more: https://docs.netbird.io/api/resources/ingress#update-an-ingress-peer
func (a *IngressAPI) Update(ctx context.Context, ingressPeerID string, request api.PutApiIngressPeersIngressPeerIdJSONRequestBody) (*api.IngressPeer, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/ingress/peers/"+ingressPeerID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPeer](resp)
return &ret, err
}
// Delete delete ingress peer
// See more: https://docs.netbird.io/api/resources/ingress#delete-an-ingress-peer
func (a *IngressAPI) Delete(ctx context.Context, ingressPeerID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/ingress/peers/"+ingressPeerID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
@@ -1,184 +0,0 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var testIngressPeer = api.IngressPeer{
Connected: true,
Enabled: true,
Id: "Test",
}
func TestIngress_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.IngressPeer{testIngressPeer})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testIngressPeer, ret[0])
})
}
func TestIngress_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/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.Ingress.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestIngress_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testIngressPeer)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Get(context.Background(), "Test")
require.NoError(t, err)
assert.Equal(t, testIngressPeer, *ret)
})
}
func TestIngress_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/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.Ingress.Get(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestIngress_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers", 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.PostApiIngressPeersJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "peer-id", req.PeerId)
retBytes, _ := json.Marshal(testIngressPeer)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Create(context.Background(), api.PostApiIngressPeersJSONRequestBody{
PeerId: "peer-id",
})
require.NoError(t, err)
assert.Equal(t, testIngressPeer, *ret)
})
}
func TestIngress_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/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.Ingress.Create(context.Background(), api.PostApiIngressPeersJSONRequestBody{
PeerId: "peer-id",
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestIngress_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/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.PutApiIngressPeersIngressPeerIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, req.Enabled)
retBytes, _ := json.Marshal(testIngressPeer)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Ingress.Update(context.Background(), "Test", api.PutApiIngressPeersIngressPeerIdJSONRequestBody{
Enabled: true,
})
require.NoError(t, err)
assert.Equal(t, testIngressPeer, *ret)
})
}
func TestIngress_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/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.Ingress.Update(context.Background(), "Test", api.PutApiIngressPeersIngressPeerIdJSONRequestBody{
Enabled: true,
})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestIngress_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/peers/Test", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Ingress.Delete(context.Background(), "Test")
require.NoError(t, err)
})
}
func TestIngress_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/ingress/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.Ingress.Delete(context.Background(), "Test")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
-92
View File
@@ -125,98 +125,6 @@ func (a *PeersAPI) CreateTemporaryAccess(ctx context.Context, peerID string, req
return &ret, err
}
// PeerIngressPortsAPI APIs for Peer Ingress Ports, do not use directly
type PeerIngressPortsAPI struct {
c *Client
peerID string
}
// IngressPorts APIs for peer ingress ports
func (a *PeersAPI) IngressPorts(peerID string) *PeerIngressPortsAPI {
return &PeerIngressPortsAPI{
c: a.c,
peerID: peerID,
}
}
// List list all ingress port allocations for a peer
// See more: https://docs.netbird.io/api/resources/peers#list-all-ingress-port-allocations
func (a *PeerIngressPortsAPI) List(ctx context.Context) ([]api.IngressPortAllocation, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/peers/"+a.peerID+"/ingress/ports", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IngressPortAllocation](resp)
return ret, err
}
// Get get ingress port allocation info
// See more: https://docs.netbird.io/api/resources/peers#retrieve-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Get(ctx context.Context, allocationID string) (*api.IngressPortAllocation, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/peers/"+a.peerID+"/ingress/ports/"+allocationID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPortAllocation](resp)
return &ret, err
}
// Create create new ingress port allocation
// See more: https://docs.netbird.io/api/resources/peers#create-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Create(ctx context.Context, request api.PostApiPeersPeerIdIngressPortsJSONRequestBody) (*api.IngressPortAllocation, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/peers/"+a.peerID+"/ingress/ports", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPortAllocation](resp)
return &ret, err
}
// Update update ingress port allocation
// See more: https://docs.netbird.io/api/resources/peers#update-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Update(ctx context.Context, allocationID string, request api.PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody) (*api.IngressPortAllocation, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/peers/"+a.peerID+"/ingress/ports/"+allocationID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.IngressPortAllocation](resp)
return &ret, err
}
// Delete delete ingress port allocation
// See more: https://docs.netbird.io/api/resources/peers#delete-an-ingress-port-allocation
func (a *PeerIngressPortsAPI) Delete(ctx context.Context, allocationID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/peers/"+a.peerID+"/ingress/ports/"+allocationID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// PeerJobsAPI APIs for Peer Jobs, do not use directly
type PeerJobsAPI struct {
c *Client
-145
View File
@@ -31,11 +31,6 @@ var (
Name: "test-peer",
}
testIngressPortAllocation = api.IngressPortAllocation{
Enabled: true,
Id: "alloc-1",
}
testJobResponse = api.JobResponse{
Id: "job-1",
Status: "pending",
@@ -221,146 +216,6 @@ func TestPeers_CreateTemporaryAccess_Err(t *testing.T) {
})
}
func TestPeerIngressPorts_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal([]api.IngressPortAllocation{testIngressPortAllocation})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testIngressPortAllocation, ret[0])
})
}
func TestPeerIngressPorts_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", 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.IngressPorts("Test").List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeerIngressPorts_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(testIngressPortAllocation)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Get(context.Background(), "alloc-1")
require.NoError(t, err)
assert.Equal(t, testIngressPortAllocation, *ret)
})
}
func TestPeerIngressPorts_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", 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.IngressPorts("Test").Get(context.Background(), "alloc-1")
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestPeerIngressPorts_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testIngressPortAllocation)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Create(context.Background(), api.PostApiPeersPeerIdIngressPortsJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testIngressPortAllocation, *ret)
})
}
func TestPeerIngressPorts_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports", 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.IngressPorts("Test").Create(context.Background(), api.PostApiPeersPeerIdIngressPortsJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPeerIngressPorts_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
retBytes, _ := json.Marshal(testIngressPortAllocation)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.Peers.IngressPorts("Test").Update(context.Background(), "alloc-1", api.PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody{})
require.NoError(t, err)
assert.Equal(t, testIngressPortAllocation, *ret)
})
}
func TestPeerIngressPorts_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", 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.IngressPorts("Test").Update(context.Background(), "alloc-1", api.PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestPeerIngressPorts_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.Peers.IngressPorts("Test").Delete(context.Background(), "alloc-1")
require.NoError(t, err)
})
}
func TestPeerIngressPorts_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/ingress/ports/alloc-1", 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.IngressPorts("Test").Delete(context.Background(), "alloc-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestPeerJobs_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/peers/Test/jobs", func(w http.ResponseWriter, r *http.Request) {
+54 -559
View File
@@ -31,9 +31,6 @@ tags:
description: View information about the account and network events.
- name: Accounts
description: View information about the accounts.
- name: Ingress Ports
description: Interact with and view information about the ingress peers and ports.
x-cloud-only: true
- name: Identity Providers
description: Interact with and view information about identity providers.
- name: Services
@@ -834,6 +831,20 @@ components:
- ssh_enabled
- login_expiration_enabled
- inactivity_expiration_enabled
NetworkAddress:
type: object
properties:
net_ip:
description: IP address with CIDR of the interface
type: string
example: 192.168.0.11/24
mac:
description: MAC address of the interface
type: string
example: "00:93:37:bd:83:0f"
required:
- net_ip
- mac
Peer:
allOf:
- $ref: '#/components/schemas/PeerMinimum'
@@ -853,6 +864,11 @@ components:
type: string
format: ipv6
example: "fd00:4e42:ab12::1"
network_addresses:
description: Network interfaces (IP + MAC) reported by the peer
type: array
items:
$ref: '#/components/schemas/NetworkAddress'
connection_ip:
description: Peer's public connection IP address
type: string
@@ -2428,222 +2444,6 @@ components:
- initiator_email
- target_id
- meta
IngressPeerCreateRequest:
type: object
properties:
peer_id:
description: ID of the peer that is used as an ingress peer
type: string
example: ch8i4ug6lnn4g9hqv7m0
enabled:
description: Defines if an ingress peer is enabled
type: boolean
example: true
fallback:
description: Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer
type: boolean
example: true
required:
- peer_id
- enabled
- fallback
IngressPeerUpdateRequest:
type: object
properties:
enabled:
description: Defines if an ingress peer is enabled
type: boolean
example: true
fallback:
description: Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer
type: boolean
example: true
required:
- enabled
- fallback
IngressPeer:
type: object
properties:
id:
description: ID of the ingress peer
type: string
example: ch8i4ug6lnn4g9hqv7m0
peer_id:
description: ID of the peer that is used as an ingress peer
type: string
example: x7p3kqf2rdd8j5zxw4n9
ingress_ip:
description: Ingress IP address of the ingress peer where the traffic arrives
type: string
example: 192.34.0.123
available_ports:
$ref: '#/components/schemas/AvailablePorts'
enabled:
description: Indicates if an ingress peer is enabled
type: boolean
example: true
connected:
description: Indicates if an ingress peer is connected to the management server
type: boolean
example: true
fallback:
description: Indicates if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer
type: boolean
example: true
region:
description: Region of the ingress peer
type: string
example: germany
required:
- id
- peer_id
- ingress_ip
- available_ports
- enabled
- connected
- fallback
- region
AvailablePorts:
type: object
properties:
tcp:
description: Number of available TCP ports left on the ingress peer
type: integer
example: 45765
udp:
description: Number of available UDP ports left on the ingress peer
type: integer
example: 50000
required:
- tcp
- udp
IngressPortAllocationRequest:
type: object
properties:
name:
description: Name of the ingress port allocation
type: string
example: Ingress Port Allocation 1
enabled:
description: Indicates if an ingress port allocation is enabled
type: boolean
example: true
port_ranges:
description: List of port ranges that are forwarded by the ingress peer
type: array
items:
$ref: '#/components/schemas/IngressPortAllocationRequestPortRange'
direct_port:
description: Direct port allocation
$ref: '#/components/schemas/IngressPortAllocationRequestDirectPort'
required:
- name
- enabled
IngressPortAllocationRequestPortRange:
type: object
properties:
start:
description: The starting port of the range of forwarded ports
type: integer
example: 80
end:
description: The ending port of the range of forwarded ports
type: integer
example: 320
protocol:
description: The protocol accepted by the port range
type: string
enum: [ "tcp", "udp", "tcp/udp" ]
example: tcp
required:
- start
- end
- protocol
IngressPortAllocationRequestDirectPort:
type: object
properties:
count:
description: The number of ports to be forwarded
type: integer
example: 5
protocol:
description: The protocol accepted by the port
type: string
enum: [ "tcp", "udp", "tcp/udp" ]
example: udp
required:
- count
- protocol
IngressPortAllocation:
type: object
properties:
id:
description: ID of the ingress port allocation
type: string
example: ch8i4ug6lnn4g9hqv7m0
name:
description: Name of the ingress port allocation
type: string
example: Ingress Peer Allocation 1
ingress_peer_id:
description: ID of the ingress peer that forwards the ports
type: string
example: x7p3kqf2rdd8j5zxw4n9
region:
description: Region of the ingress peer
type: string
example: germany
enabled:
description: Indicates if an ingress port allocation is enabled
type: boolean
example: true
ingress_ip:
description: Ingress IP address of the ingress peer where the traffic arrives
type: string
example: 192.34.0.123
port_range_mappings:
description: List of port ranges that are allowed to be used by the ingress peer
type: array
items:
$ref: '#/components/schemas/IngressPortAllocationPortMapping'
required:
- id
- name
- ingress_peer_id
- region
- enabled
- ingress_ip
- port_range_mappings
IngressPortAllocationPortMapping:
type: object
properties:
translated_start:
description: The starting port of the translated range of forwarded ports
type: integer
example: 80
translated_end:
description: The ending port of the translated range of forwarded ports
type: integer
example: 320
ingress_start:
description: The starting port of the range of ingress ports mapped to the forwarded ports
type: integer
example: 1080
ingress_end:
description: The ending port of the range of ingress ports mapped to the forwarded ports
type: integer
example: 1320
protocol:
description: Protocol accepted by the ports
type: string
enum: [ "tcp", "udp", "tcp/udp" ]
example: tcp
required:
- translated_start
- translated_end
- ingress_start
- ingress_end
- protocol
NetworkTrafficLocation:
type: object
properties:
@@ -6461,8 +6261,8 @@ components:
example: "d1m3kebd9pcs0c1pnu7g"
state:
type: string
description: Derived deployment state. `provisioning` until the gateway is rolled out and connected, `ready` while the gateway actively serves the endpoint, `failed` when the rollout reported a failure.
enum: [ "provisioning", "ready", "failed" ]
description: Derived deployment state. `provisioning` until the gateway is rolled out and connected, `ready` while the gateway actively serves the endpoint, `failed` when the rollout reported a failure, `disabled` while the gateway is turned off and the endpoint is not served.
enum: [ "provisioning", "ready", "failed", "disabled" ]
example: "ready"
endpoint:
type: string
@@ -7524,6 +7324,11 @@ paths:
schema:
type: string
description: Filter peers by IP address
- in: query
name: mac
schema:
type: string
description: Filter peers by MAC address of a network interface
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
@@ -7703,341 +7508,6 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/peers/{peerId}/ingress/ports:
get:
x-cloud-only: true
summary: List all Port Allocations
description: Returns a list of all ingress port allocations for a peer
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
- in: query
name: name
schema:
type: string
description: Filters ingress port allocations by name
responses:
'200':
description: A JSON Array of Ingress Port Allocations
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/IngressPortAllocation'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
post:
x-cloud-only: true
summary: Create a Port Allocation
description: Creates a new ingress port allocation for a peer
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
requestBody:
description: New Ingress Port Allocation request
content:
'application/json':
schema:
$ref: '#/components/schemas/IngressPortAllocationRequest'
responses:
'200':
description: A Ingress Port Allocation object
content:
application/json:
schema:
$ref: '#/components/schemas/IngressPortAllocation'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/peers/{peerId}/ingress/ports/{allocationId}:
get:
x-cloud-only: true
summary: Retrieve a Port Allocation
description: Get information about an ingress port allocation
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
- in: path
name: allocationId
required: true
schema:
type: string
description: The unique identifier of an ingress port allocation
responses:
'200':
description: A Ingress Port Allocation object
content:
application/json:
schema:
$ref: '#/components/schemas/IngressPortAllocation'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
put:
x-cloud-only: true
summary: Update a Port Allocation
description: Update information about an ingress port allocation
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
- in: path
name: allocationId
required: true
schema:
type: string
description: The unique identifier of an ingress port allocation
requestBody:
description: update an ingress port allocation
content:
application/json:
schema:
$ref: '#/components/schemas/IngressPortAllocationRequest'
responses:
'200':
description: A Ingress Port Allocation object
content:
application/json:
schema:
$ref: '#/components/schemas/IngressPortAllocation'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
delete:
x-cloud-only: true
summary: Delete a Port Allocation
description: Delete an ingress port allocation
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
- in: path
name: allocationId
required: true
schema:
type: string
description: The unique identifier of an ingress port allocation
responses:
'200':
description: Delete status code
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/ingress/peers:
get:
x-cloud-only: true
summary: List all Ingress Peers
description: Returns a list of all ingress peers
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
responses:
'200':
description: A JSON Array of Ingress Peers
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/IngressPeer'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
post:
x-cloud-only: true
summary: Create a Ingress Peer
description: Creates a new ingress peer
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
requestBody:
description: New Ingress Peer request
content:
'application/json':
schema:
$ref: '#/components/schemas/IngressPeerCreateRequest'
responses:
'200':
description: A Ingress Peer object
content:
application/json:
schema:
$ref: '#/components/schemas/IngressPeer'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/ingress/peers/{ingressPeerId}:
get:
x-cloud-only: true
summary: Retrieve a Ingress Peer
description: Get information about an ingress peer
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: ingressPeerId
required: true
schema:
type: string
description: The unique identifier of an ingress peer
responses:
'200':
description: A Ingress Peer object
content:
application/json:
schema:
$ref: '#/components/schemas/IngressPeer'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
put:
x-cloud-only: true
summary: Update a Ingress Peer
description: Update information about an ingress peer
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: ingressPeerId
required: true
schema:
type: string
description: The unique identifier of an ingress peer
requestBody:
description: update an ingress peer
content:
'application/json':
schema:
$ref: '#/components/schemas/IngressPeerUpdateRequest'
responses:
'200':
description: A Ingress Peer object
content:
application/json:
schema:
$ref: '#/components/schemas/IngressPeer'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
delete:
x-cloud-only: true
summary: Delete a Ingress Peer
description: Delete an ingress peer
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: ingressPeerId
required: true
schema:
type: string
description: The unique identifier of an ingress peer
responses:
'200':
description: Delete status code
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/setup-keys:
get:
summary: List all Setup Keys
@@ -10609,6 +10079,28 @@ paths:
$ref: "#/components/responses/requires_authentication"
"500":
$ref: "#/components/responses/internal_error"
delete:
summary: Delete MSP tenant
tags:
- MSP
parameters:
- in: path
name: id
required: true
schema:
type: string
description: The unique identifier of a tenant account
responses:
"200":
description: Successfully deleted the tenant
"400":
$ref: "#/components/responses/bad_request"
"403":
$ref: "#/components/responses/requires_authentication"
"404":
description: The tenant was not found
"500":
$ref: "#/components/responses/internal_error"
/api/integrations/msp/tenants/{id}/unlink:
post:
summary: Unlink a tenant
@@ -13355,7 +12847,7 @@ paths:
/api/reverse-proxies/domains/{domainId}:
delete:
summary: Delete a Custom domain
description: Delete an existing service custom domain
description: Delete an existing service custom domain after removing or moving all services that use it or its subdomains, including disabled services.
tags: [ Services ]
security:
- BearerAuth: [ ]
@@ -13378,6 +12870,9 @@ paths:
"$ref": "#/components/responses/forbidden"
'404':
"$ref": "#/components/responses/not_found"
'412':
description: The domain or one of its subdomains is still used by a service
content: { }
'500':
"$ref": "#/components/responses/internal_error"
/api/reverse-proxies/domains/{domainId}/validate:
@@ -13580,7 +13075,7 @@ paths:
/api/integrations/agent-network/managed-proxy:
post:
summary: Provision a managed Agent Network gateway
description: Starts provisioning of a NetBird-managed Agent Network gateway for the account, allocating its endpoint under the managed zone on the first call. Idempotent — answers 202 when this call started (or, after a failure, restarted) provisioning and 200 when a deployment already exists, reporting current state either way. Returns 409 when the account already has an Agent Network endpoint that managed provisioning does not own, and 503 when endpoint allocation is temporarily exhausted.
description: Starts provisioning of a NetBird-managed Agent Network gateway for the account, allocating its endpoint under the managed zone on the first call. Idempotent — answers 202 when this call started (or, after a failure, restarted) provisioning and 200 when a deployment already exists, reporting current state either way. A disabled deployment answers 200 with state `disabled` and stays disabled. Returns 409 when the account already has an Agent Network endpoint that managed provisioning does not own, and 503 when endpoint allocation is temporarily exhausted.
tags: [ Agent Network ]
security:
- BearerAuth: [ ]
@@ -13618,7 +13113,7 @@ paths:
$ref: '#/components/schemas/ErrorResponse'
get:
summary: Retrieve managed Agent Network gateway status
description: Reports the account's managed gateway deployment and its derived state. Returns 404 when the account has no managed deployment.
description: Reports the account's managed gateway deployment and its derived state, including `disabled` for a deployment that is turned off. Returns 404 when the account has no managed deployment.
tags: [ Agent Network ]
security:
- BearerAuth: [ ]
+22 -224
View File
@@ -79,6 +79,7 @@ func (e AgentNetworkConsumptionDimensionKind) Valid() bool {
// Defines values for AgentNetworkManagedProxyState.
const (
AgentNetworkManagedProxyStateDisabled AgentNetworkManagedProxyState = "disabled"
AgentNetworkManagedProxyStateFailed AgentNetworkManagedProxyState = "failed"
AgentNetworkManagedProxyStateProvisioning AgentNetworkManagedProxyState = "provisioning"
AgentNetworkManagedProxyStateReady AgentNetworkManagedProxyState = "ready"
@@ -87,6 +88,8 @@ const (
// Valid indicates whether the value is a known member of the AgentNetworkManagedProxyState enum.
func (e AgentNetworkManagedProxyState) Valid() bool {
switch e {
case AgentNetworkManagedProxyStateDisabled:
return true
case AgentNetworkManagedProxyStateFailed:
return true
case AgentNetworkManagedProxyStateProvisioning:
@@ -605,69 +608,6 @@ func (e IdentityProviderType) Valid() bool {
}
}
// Defines values for IngressPortAllocationPortMappingProtocol.
const (
IngressPortAllocationPortMappingProtocolTcp IngressPortAllocationPortMappingProtocol = "tcp"
IngressPortAllocationPortMappingProtocolTcpudp IngressPortAllocationPortMappingProtocol = "tcp/udp"
IngressPortAllocationPortMappingProtocolUdp IngressPortAllocationPortMappingProtocol = "udp"
)
// Valid indicates whether the value is a known member of the IngressPortAllocationPortMappingProtocol enum.
func (e IngressPortAllocationPortMappingProtocol) Valid() bool {
switch e {
case IngressPortAllocationPortMappingProtocolTcp:
return true
case IngressPortAllocationPortMappingProtocolTcpudp:
return true
case IngressPortAllocationPortMappingProtocolUdp:
return true
default:
return false
}
}
// Defines values for IngressPortAllocationRequestDirectPortProtocol.
const (
IngressPortAllocationRequestDirectPortProtocolTcp IngressPortAllocationRequestDirectPortProtocol = "tcp"
IngressPortAllocationRequestDirectPortProtocolTcpudp IngressPortAllocationRequestDirectPortProtocol = "tcp/udp"
IngressPortAllocationRequestDirectPortProtocolUdp IngressPortAllocationRequestDirectPortProtocol = "udp"
)
// Valid indicates whether the value is a known member of the IngressPortAllocationRequestDirectPortProtocol enum.
func (e IngressPortAllocationRequestDirectPortProtocol) Valid() bool {
switch e {
case IngressPortAllocationRequestDirectPortProtocolTcp:
return true
case IngressPortAllocationRequestDirectPortProtocolTcpudp:
return true
case IngressPortAllocationRequestDirectPortProtocolUdp:
return true
default:
return false
}
}
// Defines values for IngressPortAllocationRequestPortRangeProtocol.
const (
IngressPortAllocationRequestPortRangeProtocolTcp IngressPortAllocationRequestPortRangeProtocol = "tcp"
IngressPortAllocationRequestPortRangeProtocolTcpudp IngressPortAllocationRequestPortRangeProtocol = "tcp/udp"
IngressPortAllocationRequestPortRangeProtocolUdp IngressPortAllocationRequestPortRangeProtocol = "udp"
)
// Valid indicates whether the value is a known member of the IngressPortAllocationRequestPortRangeProtocol enum.
func (e IngressPortAllocationRequestPortRangeProtocol) Valid() bool {
switch e {
case IngressPortAllocationRequestPortRangeProtocolTcp:
return true
case IngressPortAllocationRequestPortRangeProtocolTcpudp:
return true
case IngressPortAllocationRequestPortRangeProtocolUdp:
return true
default:
return false
}
}
// Defines values for IntegrationResponsePlatform.
const (
IntegrationResponsePlatformDatadog IntegrationResponsePlatform = "datadog"
@@ -2265,11 +2205,11 @@ type AgentNetworkManagedProxy struct {
// Region Region of the cluster hosting the deployment.
Region *string `json:"region,omitempty"`
// State Derived deployment state. `provisioning` until the gateway is rolled out and connected, `ready` while the gateway actively serves the endpoint, `failed` when the rollout reported a failure.
// State Derived deployment state. `provisioning` until the gateway is rolled out and connected, `ready` while the gateway actively serves the endpoint, `failed` when the rollout reported a failure, `disabled` while the gateway is turned off and the endpoint is not served.
State AgentNetworkManagedProxyState `json:"state"`
}
// AgentNetworkManagedProxyState Derived deployment state. `provisioning` until the gateway is rolled out and connected, `ready` while the gateway actively serves the endpoint, `failed` when the rollout reported a failure.
// AgentNetworkManagedProxyState Derived deployment state. `provisioning` until the gateway is rolled out and connected, `ready` while the gateway actively serves the endpoint, `failed` when the rollout reported a failure, `disabled` while the gateway is turned off and the endpoint is not served.
type AgentNetworkManagedProxyState string
// AgentNetworkManagedProxyConflict Conflict body returned when the account already has an Agent Network endpoint that managed provisioning does not own, naming that endpoint.
@@ -2603,15 +2543,6 @@ type AgentNetworkUsageBucket struct {
TotalTokens int64 `json:"total_tokens"`
}
// AvailablePorts defines model for AvailablePorts.
type AvailablePorts struct {
// Tcp Number of available TCP ports left on the ingress peer
Tcp int `json:"tcp"`
// Udp Number of available UDP ports left on the ingress peer
Udp int `json:"udp"`
}
// AzureIntegration defines model for AzureIntegration.
type AzureIntegration struct {
// ClientId Azure AD application (client) ID
@@ -3464,139 +3395,6 @@ type IdpIntegrationSyncLog struct {
Timestamp time.Time `json:"timestamp"`
}
// IngressPeer defines model for IngressPeer.
type IngressPeer struct {
AvailablePorts AvailablePorts `json:"available_ports"`
// Connected Indicates if an ingress peer is connected to the management server
Connected bool `json:"connected"`
// Enabled Indicates if an ingress peer is enabled
Enabled bool `json:"enabled"`
// Fallback Indicates if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer
Fallback bool `json:"fallback"`
// Id ID of the ingress peer
Id string `json:"id"`
// IngressIp Ingress IP address of the ingress peer where the traffic arrives
IngressIp string `json:"ingress_ip"`
// PeerId ID of the peer that is used as an ingress peer
PeerId string `json:"peer_id"`
// Region Region of the ingress peer
Region string `json:"region"`
}
// IngressPeerCreateRequest defines model for IngressPeerCreateRequest.
type IngressPeerCreateRequest struct {
// Enabled Defines if an ingress peer is enabled
Enabled bool `json:"enabled"`
// Fallback Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer
Fallback bool `json:"fallback"`
// PeerId ID of the peer that is used as an ingress peer
PeerId string `json:"peer_id"`
}
// IngressPeerUpdateRequest defines model for IngressPeerUpdateRequest.
type IngressPeerUpdateRequest struct {
// Enabled Defines if an ingress peer is enabled
Enabled bool `json:"enabled"`
// Fallback Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer
Fallback bool `json:"fallback"`
}
// IngressPortAllocation defines model for IngressPortAllocation.
type IngressPortAllocation struct {
// Enabled Indicates if an ingress port allocation is enabled
Enabled bool `json:"enabled"`
// Id ID of the ingress port allocation
Id string `json:"id"`
// IngressIp Ingress IP address of the ingress peer where the traffic arrives
IngressIp string `json:"ingress_ip"`
// IngressPeerId ID of the ingress peer that forwards the ports
IngressPeerId string `json:"ingress_peer_id"`
// Name Name of the ingress port allocation
Name string `json:"name"`
// PortRangeMappings List of port ranges that are allowed to be used by the ingress peer
PortRangeMappings []IngressPortAllocationPortMapping `json:"port_range_mappings"`
// Region Region of the ingress peer
Region string `json:"region"`
}
// IngressPortAllocationPortMapping defines model for IngressPortAllocationPortMapping.
type IngressPortAllocationPortMapping struct {
// IngressEnd The ending port of the range of ingress ports mapped to the forwarded ports
IngressEnd int `json:"ingress_end"`
// IngressStart The starting port of the range of ingress ports mapped to the forwarded ports
IngressStart int `json:"ingress_start"`
// Protocol Protocol accepted by the ports
Protocol IngressPortAllocationPortMappingProtocol `json:"protocol"`
// TranslatedEnd The ending port of the translated range of forwarded ports
TranslatedEnd int `json:"translated_end"`
// TranslatedStart The starting port of the translated range of forwarded ports
TranslatedStart int `json:"translated_start"`
}
// IngressPortAllocationPortMappingProtocol Protocol accepted by the ports
type IngressPortAllocationPortMappingProtocol string
// IngressPortAllocationRequest defines model for IngressPortAllocationRequest.
type IngressPortAllocationRequest struct {
DirectPort *IngressPortAllocationRequestDirectPort `json:"direct_port,omitempty"`
// Enabled Indicates if an ingress port allocation is enabled
Enabled bool `json:"enabled"`
// Name Name of the ingress port allocation
Name string `json:"name"`
// PortRanges List of port ranges that are forwarded by the ingress peer
PortRanges *[]IngressPortAllocationRequestPortRange `json:"port_ranges,omitempty"`
}
// IngressPortAllocationRequestDirectPort defines model for IngressPortAllocationRequestDirectPort.
type IngressPortAllocationRequestDirectPort struct {
// Count The number of ports to be forwarded
Count int `json:"count"`
// Protocol The protocol accepted by the port
Protocol IngressPortAllocationRequestDirectPortProtocol `json:"protocol"`
}
// IngressPortAllocationRequestDirectPortProtocol The protocol accepted by the port
type IngressPortAllocationRequestDirectPortProtocol string
// IngressPortAllocationRequestPortRange defines model for IngressPortAllocationRequestPortRange.
type IngressPortAllocationRequestPortRange struct {
// End The ending port of the range of forwarded ports
End int `json:"end"`
// Protocol The protocol accepted by the port range
Protocol IngressPortAllocationRequestPortRangeProtocol `json:"protocol"`
// Start The starting port of the range of forwarded ports
Start int `json:"start"`
}
// IngressPortAllocationRequestPortRangeProtocol The protocol accepted by the port range
type IngressPortAllocationRequestPortRangeProtocol string
// InstanceStatus Instance status information
type InstanceStatus struct {
// SetupRequired Indicates whether the instance requires initial setup
@@ -3832,6 +3630,15 @@ type Network struct {
RoutingPeersCount int `json:"routing_peers_count"`
}
// NetworkAddress defines model for NetworkAddress.
type NetworkAddress struct {
// Mac MAC address of the interface
Mac string `json:"mac"`
// NetIp IP address with CIDR of the interface
NetIp string `json:"net_ip"`
}
// NetworkRequest defines model for NetworkRequest.
type NetworkRequest struct {
// Description Network description
@@ -4281,6 +4088,9 @@ type Peer struct {
// Name Peer's hostname
Name string `json:"name"`
// NetworkAddresses Network interfaces (IP + MAC) reported by the peer
NetworkAddresses *[]NetworkAddress `json:"network_addresses,omitempty"`
// Os Peer's operating system and version
Os string `json:"os"`
@@ -4375,6 +4185,9 @@ type PeerBatch struct {
// Name Peer's hostname
Name string `json:"name"`
// NetworkAddresses Network interfaces (IP + MAC) reported by the peer
NetworkAddresses *[]NetworkAddress `json:"network_addresses,omitempty"`
// Os Peer's operating system and version
Os string `json:"os"`
@@ -6297,12 +6110,9 @@ type GetApiPeersParams struct {
// Ip Filter peers by IP address
Ip *string `form:"ip,omitempty" json:"ip,omitempty"`
}
// GetApiPeersPeerIdIngressPortsParams defines parameters for GetApiPeersPeerIdIngressPorts.
type GetApiPeersPeerIdIngressPortsParams struct {
// Name Filters ingress port allocations by name
Name *string `form:"name,omitempty" json:"name,omitempty"`
// Mac Filter peers by MAC address of a network interface
Mac *string `form:"mac,omitempty" json:"mac,omitempty"`
}
// GetApiUsersParams defines parameters for GetApiUsers.
@@ -6386,12 +6196,6 @@ type PostApiIdentityProvidersJSONRequestBody = IdentityProviderRequest
// PutApiIdentityProvidersIdpIdJSONRequestBody defines body for PutApiIdentityProvidersIdpId for application/json ContentType.
type PutApiIdentityProvidersIdpIdJSONRequestBody = IdentityProviderRequest
// PostApiIngressPeersJSONRequestBody defines body for PostApiIngressPeers for application/json ContentType.
type PostApiIngressPeersJSONRequestBody = IngressPeerCreateRequest
// PutApiIngressPeersIngressPeerIdJSONRequestBody defines body for PutApiIngressPeersIngressPeerId for application/json ContentType.
type PutApiIngressPeersIngressPeerIdJSONRequestBody = IngressPeerUpdateRequest
// CreateAzureIntegrationJSONRequestBody defines body for CreateAzureIntegration for application/json ContentType.
type CreateAzureIntegrationJSONRequestBody = CreateAzureIntegrationRequest
@@ -6500,12 +6304,6 @@ type PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody = NetworkRouterReques
// PutApiPeersPeerIdJSONRequestBody defines body for PutApiPeersPeerId for application/json ContentType.
type PutApiPeersPeerIdJSONRequestBody = PeerRequest
// PostApiPeersPeerIdIngressPortsJSONRequestBody defines body for PostApiPeersPeerIdIngressPorts for application/json ContentType.
type PostApiPeersPeerIdIngressPortsJSONRequestBody = IngressPortAllocationRequest
// PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody defines body for PutApiPeersPeerIdIngressPortsAllocationId for application/json ContentType.
type PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody = IngressPortAllocationRequest
// PostApiPeersPeerIdJobsJSONRequestBody defines body for PostApiPeersPeerIdJobs for application/json ContentType.
type PostApiPeersPeerIdJobsJSONRequestBody = JobRequest
@@ -0,0 +1,997 @@
openapi: 3.1.0
servers:
- url: /api/v1alpha1
description: Default server
info:
title: NetBird REST API
description: API to manipulate groups, rules, policies and retrieve information about peers and users
version: 1.0.0-alpha1
tags:
- name: Users
description: Interact with and view information about users.
- name: Tokens
description: Interact with and view information about tokens.
- name: Peers
description: Interact with and view information about peers.
- name: Setup Keys
description: Interact with and view information about setup keys.
- name: Groups
description: Interact with and view information about groups.
- name: Policies
description: Interact with and view information about policies.
- name: Posture Checks
description: Interact with and view information about posture checks.
- name: Routes
description: Interact with and view information about routes.
- name: DNS
description: Interact with and view information about DNS configuration.
- name: DNS Zones
description: Interact with and view information about custom DNS zones.
- name: Events
description: View information about the account and network events.
- name: Accounts
description: View information about the accounts.
- name: Ingress Ports
description: Interact with and view information about the ingress peers and ports.
x-cloud-only: true
- name: Identity Providers
description: Interact with and view information about identity providers.
- name: Services
description: Interact with and view information about reverse proxy services.
- name: Instance
description: Instance setup and status endpoints for initial configuration.
- name: Jobs
description: Interact with and view information about remote jobs.
x-experimental: true
- name: Usage
description: Retrieve current usage statistics for the account.
x-cloud-only: true
- name: Subscription
description: Manage and view information about account subscriptions.
x-cloud-only: true
- name: Plans
description: Retrieve available plans and products.
x-cloud-only: true
- name: Checkout
description: Manage checkout sessions for plan subscriptions.
x-cloud-only: true
- name: AWS Marketplace
description: Manage AWS Marketplace subscriptions.
x-cloud-only: true
- name: Portal
description: Access customer portal for subscription management.
x-cloud-only: true
- name: Invoice
description: Manage and retrieve account invoices.
x-cloud-only: true
- name: MSP
description: MSP portal for Tenant management.
x-cloud-only: true
- name: IDP SCIM Integrations
description: Manage generic SCIM identity provider integrations for user and group sync.
x-cloud-only: true
- name: IDP Google Integrations
description: Manage Google Workspace identity provider integrations for user and group sync.
x-cloud-only: true
- name: IDP Azure Integrations
description: Manage Azure AD identity provider integrations for user and group sync.
x-cloud-only: true
- name: IDP Okta SCIM Integrations
description: Manage Okta SCIM identity provider integrations for user and group sync.
x-cloud-only: true
- name: EDR Intune Integrations
description: Manage Microsoft Intune EDR integrations.
x-cloud-only: true
- name: EDR SentinelOne Integrations
description: Manage SentinelOne EDR integrations.
x-cloud-only: true
- name: EDR Falcon Integrations
description: Manage CrowdStrike Falcon EDR integrations.
x-cloud-only: true
- name: EDR Huntress Integrations
description: Manage Huntress EDR integrations.
x-cloud-only: true
- name: EDR FleetDM Integrations
description: Manage FleetDM EDR integrations.
x-cloud-only: true
- name: EDR Peers
description: Manage EDR compliance bypass for peers.
x-cloud-only: true
- name: Event Streaming Integrations
description: Manage event streaming integrations.
x-cloud-only: true
- name: Notifications
description: Manage notification channels for account event alerts.
x-cloud-only: true
components:
schemas:
CountryCode:
description: 2-letter ISO 3166-1 alpha-2 code that represents the country
type: string
example: "DE"
CityName:
description: Commonly used English name of the city
type: string
example: "Berlin"
Country:
description: Describe country geographical location information
type: object
properties:
country_name:
description: Commonly used English name of the country
type: string
example: "Germany"
country_code:
$ref: '#/components/schemas/CountryCode'
required:
- country_name
- country_code
City:
description: Describe city geographical location information
type: object
properties:
geoname_id:
description: Integer ID of the record in GeoNames database
type: integer
example: 2950158
city_name:
description: Commonly used English name of the city
type: string
example: "Berlin"
required:
- geoname_id
- city_name
ErrorResponse:
type: object
description: Standard error response
properties:
message:
type: string
description: A human-readable error message.
example: "couldn't parse JSON request"
PeerBatch:
allOf:
- $ref: '#/components/schemas/Peer'
- type: object
properties:
created_at:
description: Peer creation date (UTC)
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
accessible_peers_count:
description: Number of accessible peers
type: integer
example: 5
required:
- created_at
- accessible_peers_count
PeerMinimum:
type: object
properties:
id:
description: Peer ID
type: string
example: chacbco6lnnbn6cg5s90
name:
description: Peer's hostname
type: string
example: stage-host-1
required:
- id
- name
GroupMinimum:
type: object
properties:
id:
description: Group ID
type: string
example: ch8i4ug6lnn4g9hqv7m0
name:
description: Group Name identifier
type: string
example: devs
peers_count:
description: Count of peers associated to the group
type: integer
example: 2
resources_count:
description: Count of resources associated to the group
type: integer
example: 5
issued:
description: How the group was issued (api, integration, jwt)
type: string
enum:
- "api"
- "integration"
- "jwt"
example: api
required:
- id
- name
- peers_count
- resources_count
PeerLocalFlags:
type: object
properties:
rosenpass_enabled:
description: Indicates whether Rosenpass is enabled on this peer
type: boolean
example: true
rosenpass_permissive:
description: Indicates whether Rosenpass is in permissive mode or not
type: boolean
example: false
server_ssh_allowed:
description: Indicates whether SSH access this peer is allowed or not
type: boolean
example: true
remote_jobs_allowed:
description: Indicates whether the peer has opted into management-requested remote jobs (e.g. debug bundles)
type: boolean
example: true
disable_client_routes:
description: Indicates whether client routes are disabled on this peer or not
type: boolean
example: false
disable_server_routes:
description: Indicates whether server routes are disabled on this peer or not
type: boolean
example: false
disable_dns:
description: Indicates whether DNS management is disabled on this peer or not
type: boolean
example: false
disable_firewall:
description: Indicates whether firewall management is disabled on this peer or not
type: boolean
example: false
block_lan_access:
description: Indicates whether LAN access is blocked on this peer when used as a routing peer
type: boolean
example: false
block_inbound:
description: Indicates whether inbound traffic is blocked on this peer
type: boolean
example: false
lazy_connection_enabled:
description: Indicates whether lazy connection is enabled on this peer
type: boolean
example: false
Peer:
allOf:
- $ref: '#/components/schemas/PeerMinimum'
- type: object
properties:
created_at:
description: Peer creation date (UTC)
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
ip:
description: Peer's IP address
type: string
example: 10.64.0.1
ipv6:
description: Peer's IPv6 overlay address
type: string
format: ipv6
example: "fd00:4e42:ab12::1"
connection_ip:
description: Peer's public connection IP address
type: string
example: 35.64.0.1
connected:
description: Peer to Management connection status
type: boolean
example: true
last_seen:
description: Last time peer connected to Netbird's management service
type: string
format: date-time
example: "2023-05-05T10:05:26.420578Z"
os:
description: Peer's operating system and version
type: string
example: Darwin 13.2.1
kernel_version:
description: Peer's operating system kernel version
type: string
example: 23.2.0
geoname_id:
description: Unique identifier from the GeoNames database for a specific geographical location.
type: integer
example: 2643743
version:
description: Peer's daemon or cli version
type: string
example: 0.14.0
groups:
description: Groups that the peer belongs to
type: array
items:
$ref: '#/components/schemas/GroupMinimum'
ssh_enabled:
description: Indicates whether SSH server is enabled on this peer
type: boolean
example: true
user_id:
description: User ID of the user that enrolled this peer
type: string
example: google-oauth2|277474792786460067937
hostname:
description: Hostname of the machine
type: string
example: stage-host-1
ui_version:
description: Peer's desktop UI version
type: string
example: 0.14.0
dns_label:
description: Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
type: string
example: stage-host-1.netbird.cloud
login_expiration_enabled:
description: Indicates whether peer login expiration has been enabled or not
type: boolean
example: false
login_expired:
description: Indicates whether peer's login expired or not
type: boolean
example: false
last_login:
description: Last time this peer performed log in (authentication). E.g., user authenticated.
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
inactivity_expiration_enabled:
description: Indicates whether peer inactivity expiration has been enabled or not
type: boolean
example: false
approval_required:
description: (Cloud only) Indicates whether peer needs approval
type: boolean
example: true
disapproval_reason:
description: (Cloud only) Reason why the peer requires approval
type: string
country_code:
$ref: '#/components/schemas/CountryCode'
city_name:
$ref: '#/components/schemas/CityName'
serial_number:
description: System serial number
type: string
example: "C02XJ0J0JGH7"
extra_dns_labels:
description: Extra DNS labels added to the peer
type: array
items:
type: string
example: "stage-host-1"
ephemeral:
description: Indicates whether the peer is ephemeral or not
type: boolean
example: false
local_flags:
$ref: '#/components/schemas/PeerLocalFlags'
required:
- city_name
- connected
- connection_ip
- country_code
- created_at
- dns_label
- geoname_id
- groups
- hostname
- ip
- kernel_version
- last_login
- last_seen
- login_expiration_enabled
- login_expired
- inactivity_expiration_enabled
- os
- ssh_enabled
- user_id
- version
- ui_version
- approval_required
- serial_number
- extra_dns_labels
- ephemeral
PeerRequest:
type: object
properties:
name:
type: string
example: stage-host-1
ssh_enabled:
type: boolean
example: true
login_expiration_enabled:
type: boolean
example: false
inactivity_expiration_enabled:
type: boolean
example: false
approval_required:
description: (Cloud only) Indicates whether peer needs approval
type: boolean
example: true
ip:
description: Peer's IP address
type: string
format: ipv4
example: 100.64.0.15
ipv6:
description: Peer's IPv6 overlay address. Omitted if IPv6 is not enabled for the account.
type: string
format: ipv6
example: "fd00:4e42:ab12::1"
required:
- name
- ssh_enabled
- login_expiration_enabled
- inactivity_expiration_enabled
User:
type: object
properties:
id:
description: User ID
type: string
example: google-oauth2|277474792786460067937
email:
description: User's email address
type: string
example: demo@netbird.io
password:
description: User's password. Only present when user is created (create user endpoint is called) and only when IdP supports user creation with password.
type: string
example: super_secure_password
name:
description: User's name from idp provider
type: string
example: Tom Schulz
role:
description: User's NetBird account role
type: string
example: admin
status:
description: User's status
type: string
enum:
- "active"
- "invited"
- "blocked"
example: active
last_login:
description: Last time this user performed a login to the dashboard
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
auto_groups:
description: Group IDs to auto-assign to peers registered by this user
type: array
items:
type: string
example: ch8i4ug6lnn4g9hqv7m0
is_current:
description: Is true if authenticated user is the same as this user
type: boolean
readOnly: true
example: true
is_service_user:
description: Is true if this user is a service user
type: boolean
readOnly: true
example: false
is_blocked:
description: Is true if this user is blocked. Blocked users can't use the system
type: boolean
example: false
pending_approval:
description: Is true if this user requires approval before being activated. Only applicable for users joining via domain matching when user_approval_required is enabled.
type: boolean
example: false
issued:
description: How user was issued by API or Integration
type: string
example: api
idp_id:
description: Identity provider ID (connector ID) that the user authenticated with. Only populated for users with Dex-encoded user IDs.
type: string
example: okta-abc123
permissions:
$ref: '#/components/schemas/UserPermissions'
required:
- id
- email
- name
- role
- auto_groups
- status
- is_blocked
- pending_approval
UserCreateRequest:
type: object
properties:
email:
description: User's Email to send invite to
type: string
example: demo@netbird.io
name:
description: User's full name
type: string
example: Tom Schulz
role:
description: User's NetBird account role
type: string
example: admin
auto_groups:
description: Group IDs to auto-assign to peers registered by this user
type: array
items:
type: string
example: ch8i4ug6lnn4g9hqv7m0
is_service_user:
description: Is true if this user is a service user
type: boolean
example: false
required:
- role
- auto_groups
- is_service_user
UserRequest:
type: object
properties:
role:
description: User's NetBird account role
type: string
example: admin
auto_groups:
description: Group IDs to auto-assign to peers registered by this user
type: array
items:
type: string
example: ch8i4ug6lnn4g9hqv7m0
is_blocked:
description: If set to true then user is blocked and can't use the system
type: boolean
example: false
required:
- role
- auto_groups
- is_blocked
UserPermissions:
type: object
properties:
is_restricted:
type: boolean
description: Indicates whether this User's Peers view is restricted
modules:
type: object
additionalProperties:
type: object
additionalProperties:
type: boolean
propertyNames:
type: string
description: The operation type
propertyNames:
type: string
description: The module name
example: {"networks": {"read": true, "create": false, "update": false, "delete": false}, "peers": {"read": false, "create": false, "update": false, "delete": false}}
required:
- modules
- is_restricted
responses:
not_found:
description: Resource not found
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: {}
validation_failed_simple:
description: Validation failed
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: {}
bad_request:
description: Bad Request
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: {}
unprocessable:
description: Unprocessable Entity
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content:
application/json:
schema:
type: object
properties:
message:
type: string
code:
type: integer
required:
- message
- code
internal_error:
description: Internal Server Error
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: {}
validation_failed:
description: Validation failed
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: {}
forbidden:
description: Forbidden
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: {}
requires_authentication:
description: Requires authentication
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: {}
conflict:
description: Conflict
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
headers:
X-Request-Id:
description: |
Unique identifier assigned to the request by the server and set on every
response. Useful for correlating client requests with server-side logs.
schema:
type: string
example: cot7r4n3l3vh3qj4qveg
example: cot7r4n3l3vh3qj4qveg
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
TokenAuth:
type: apiKey
in: header
name: Authorization
description: >-
Enter the token with the `Token` prefix, e.g. "Token nbp_F3f0d.....".
pathItems:
peers:
get:
summary: List all Peers
description: Returns a list of all peers
tags:
- Peers
parameters:
- name: page
in: query
description: Page number
required: false
schema:
type: integer
minimum: 1
default: 1
- name: page_size
in: query
description: Number of peers per page
required: false
schema:
type: integer
minimum: 1
maximum: 250
default: 100
- name: connected
in: query
description: Filter by peer connected status
required: false
schema:
type: boolean
- name: approval_required
in: query
description: Filter by approval_required field
required: false
schema:
type: boolean
- name: os
in: query
description: Peer os
required: false
schema:
type: array
items:
type: string
enum:
- linux
- windows
- mac
- android
- ios
- js
- name: search
in: query
description: filter peers by name, dns_label, ip, os, version, serial_number, owner name, owner email, group names, mac
required: false
schema:
type: string
security:
- BearerAuth: []
- TokenAuth: []
responses:
'200':
description: A JSON Array of Peers
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/PeerBatch'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'422':
"$ref": "#/components/responses/unprocessable"
'500':
"$ref": "#/components/responses/internal_error"
peer:
get:
summary: Retrieve a Peer
description: Get information about a peer
tags:
- Peers
security:
- BearerAuth: []
- TokenAuth: []
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
responses:
'200':
description: A Peer object
content:
application/json:
schema:
$ref: '#/components/schemas/Peer'
'400':
"$ref": '#/components/responses/bad_request'
'401':
"$ref": '#/components/responses/requires_authentication'
'403':
"$ref": '#/components/responses/forbidden'
'500':
"$ref": '#/components/responses/internal_error'
put:
summary: Update a Peer
description: Update information about a peer
tags:
- Peers
security:
- BearerAuth: []
- TokenAuth: []
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
requestBody:
description: update a peer
required: true
content:
'application/json':
schema:
$ref: '#/components/schemas/PeerRequest'
responses:
'200':
description: A Peer object
content:
application/json:
schema:
$ref: '#/components/schemas/Peer'
'400':
"$ref": '#/components/responses/bad_request'
'401':
"$ref": '#/components/responses/requires_authentication'
'403':
"$ref": '#/components/responses/forbidden'
'422':
"$ref": "#/components/responses/unprocessable"
'500':
"$ref": '#/components/responses/internal_error'
delete:
summary: Delete a Peer
description: Delete a peer
tags:
- Peers
security:
- BearerAuth: []
- TokenAuth: []
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
responses:
'200':
description: Delete status code
content: {}
'400':
"$ref": '#/components/responses/bad_request'
'401':
"$ref": '#/components/responses/requires_authentication'
'403':
"$ref": '#/components/responses/forbidden'
'500':
"$ref": '#/components/responses/internal_error'
UsersPath:
get:
summary: List all Users
description: Returns a list of all users
tags:
- Users
security:
- BearerAuth: []
- TokenAuth: []
parameters:
- in: query
name: service_user
schema:
type: boolean
description: Filters users and returns either regular users or service users
responses:
'200':
description: A JSON array of Users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'422':
"$ref": "#/components/responses/unprocessable"
'500':
"$ref": "#/components/responses/internal_error"
post:
summary: Create a User
description: Creates a new service user or sends an invite to a regular user
tags:
- Users
security:
- BearerAuth: []
- TokenAuth: []
requestBody:
description: User invite information
required: true
content:
'application/json':
schema:
$ref: '#/components/schemas/UserCreateRequest'
responses:
'200':
description: A User object
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'422':
"$ref": "#/components/responses/unprocessable"
'500':
"$ref": "#/components/responses/internal_error"
UserPath:
put:
summary: Update a User
description: Update information about a User
tags:
- Users
security:
- BearerAuth: []
- TokenAuth: []
parameters:
- in: path
name: userId
required: true
schema:
type: string
description: The unique identifier of a user
requestBody:
description: User update
required: true
content:
'application/json':
schema:
$ref: '#/components/schemas/UserRequest'
responses:
'200':
description: A User object
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'422':
"$ref": "#/components/responses/unprocessable"
'500':
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a User
description: This method removes a user from accessing the system. For this leaves the IDP user intact unless the `--user-delete-from-idp` is passed to management startup.
tags:
- Users
security:
- BearerAuth: []
- TokenAuth: []
parameters:
- in: path
name: userId
required: true
schema:
type: string
description: The unique identifier of a user
responses:
'200':
description: Delete status code
content: {}
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
paths:
/peers:
$ref: '#/components/pathItems/peers'
/peers/{peerId}:
$ref: '#/components/pathItems/peer'
/users:
$ref: '#/components/pathItems/UsersPath'
/users/{userId}:
$ref: '#/components/pathItems/UserPath'
@@ -0,0 +1,31 @@
components:
schemas:
GroupMinimum:
type: object
properties:
id:
description: Group ID
type: string
example: ch8i4ug6lnn4g9hqv7m0
name:
description: Group Name identifier
type: string
example: devs
peers_count:
description: Count of peers associated to the group
type: integer
example: 2
resources_count:
description: Count of resources associated to the group
type: integer
example: 5
issued:
description: How the group was issued (api, integration, jwt)
type: string
enum: [ "api", "integration", "jwt" ]
example: api
required:
- id
- name
- peers_count
- resources_count
@@ -0,0 +1,250 @@
openapi: 3.1.0
servers:
- url: /api/v1alpha1
description: Default server
info:
title: NetBird REST API
description: API to manipulate groups, rules, policies and retrieve information about peers and users
version: 1.0.0-alpha1
tags:
- name: Users
description: Interact with and view information about users.
- name: Tokens
description: Interact with and view information about tokens.
- name: Peers
description: Interact with and view information about peers.
- name: Setup Keys
description: Interact with and view information about setup keys.
- name: Groups
description: Interact with and view information about groups.
- name: Policies
description: Interact with and view information about policies.
- name: Posture Checks
description: Interact with and view information about posture checks.
- name: Routes
description: Interact with and view information about routes.
- name: DNS
description: Interact with and view information about DNS configuration.
- name: DNS Zones
description: Interact with and view information about custom DNS zones.
- name: Events
description: View information about the account and network events.
- name: Accounts
description: View information about the accounts.
- name: Ingress Ports
description: Interact with and view information about the ingress peers and ports.
x-cloud-only: true
- name: Identity Providers
description: Interact with and view information about identity providers.
- name: Services
description: Interact with and view information about reverse proxy services.
- name: Instance
description: Instance setup and status endpoints for initial configuration.
- name: Jobs
description: Interact with and view information about remote jobs.
x-experimental: true
- name: Usage
description: Retrieve current usage statistics for the account.
x-cloud-only: true
- name: Subscription
description: Manage and view information about account subscriptions.
x-cloud-only: true
- name: Plans
description: Retrieve available plans and products.
x-cloud-only: true
- name: Checkout
description: Manage checkout sessions for plan subscriptions.
x-cloud-only: true
- name: AWS Marketplace
description: Manage AWS Marketplace subscriptions.
x-cloud-only: true
- name: Portal
description: Access customer portal for subscription management.
x-cloud-only: true
- name: Invoice
description: Manage and retrieve account invoices.
x-cloud-only: true
- name: MSP
description: MSP portal for Tenant management.
x-cloud-only: true
- name: IDP SCIM Integrations
description: Manage generic SCIM identity provider integrations for user and group sync.
x-cloud-only: true
- name: IDP Google Integrations
description: Manage Google Workspace identity provider integrations for user and group sync.
x-cloud-only: true
- name: IDP Azure Integrations
description: Manage Azure AD identity provider integrations for user and group sync.
x-cloud-only: true
- name: IDP Okta SCIM Integrations
description: Manage Okta SCIM identity provider integrations for user and group sync.
x-cloud-only: true
- name: EDR Intune Integrations
description: Manage Microsoft Intune EDR integrations.
x-cloud-only: true
- name: EDR SentinelOne Integrations
description: Manage SentinelOne EDR integrations.
x-cloud-only: true
- name: EDR Falcon Integrations
description: Manage CrowdStrike Falcon EDR integrations.
x-cloud-only: true
- name: EDR Huntress Integrations
description: Manage Huntress EDR integrations.
x-cloud-only: true
- name: EDR FleetDM Integrations
description: Manage FleetDM EDR integrations.
x-cloud-only: true
- name: EDR Peers
description: Manage EDR compliance bypass for peers.
x-cloud-only: true
- name: Event Streaming Integrations
description: Manage event streaming integrations.
x-cloud-only: true
- name: Notifications
description: Manage notification channels for account event alerts.
x-cloud-only: true
components:
schemas:
CountryCode:
description: 2-letter ISO 3166-1 alpha-2 code that represents the country
type: string
example: "DE"
CityName:
description: Commonly used English name of the city
type: string
example: "Berlin"
Country:
description: Describe country geographical location information
type: object
properties:
country_name:
description: Commonly used English name of the country
type: string
example: "Germany"
country_code:
$ref: '#/components/schemas/CountryCode'
required:
- country_name
- country_code
City:
description: Describe city geographical location information
type: object
properties:
geoname_id:
description: Integer ID of the record in GeoNames database
type: integer
example: 2950158
city_name:
description: Commonly used English name of the city
type: string
example: "Berlin"
required:
- geoname_id
- city_name
ErrorResponse:
type: object
description: Standard error response
properties:
message:
type: string
description: A human-readable error message.
example: "couldn't parse JSON request"
responses:
not_found:
description: Resource not found
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: { }
validation_failed_simple:
description: Validation failed
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: { }
bad_request:
description: Bad Request
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: { }
unprocessable:
description: Unprocessable Entity
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content:
application/json:
schema:
type: object
properties:
message:
type: string
code:
type: integer
required:
- message
- code
internal_error:
description: Internal Server Error
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: { }
validation_failed:
description: Validation failed
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: { }
forbidden:
description: Forbidden
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: { }
requires_authentication:
description: Requires authentication
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content: { }
conflict:
description: Conflict
headers:
X-Request-Id:
$ref: '#/components/headers/X-Request-Id'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
headers:
X-Request-Id:
description: |
Unique identifier assigned to the request by the server and set on every
response. Useful for correlating client requests with server-side logs.
schema:
type: string
example: cot7r4n3l3vh3qj4qveg
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
TokenAuth:
type: apiKey
in: header
name: Authorization
description: >-
Enter the token with the `Token` prefix, e.g. "Token nbp_F3f0d.....".
paths:
/peers:
$ref: './peer/peers.yaml'
/peers/{peerId}:
$ref: './peer/peer.yaml'
/users:
$ref: './user/user_paths.yaml#/UsersPath'
/users/{userId}:
$ref: './user/user_paths.yaml#/UserPath'
@@ -0,0 +1,257 @@
components:
schemas:
PeerRequest:
type: object
properties:
name:
type: string
example: stage-host-1
ssh_enabled:
type: boolean
example: true
login_expiration_enabled:
type: boolean
example: false
inactivity_expiration_enabled:
type: boolean
example: false
approval_required:
description: (Cloud only) Indicates whether peer needs approval
type: boolean
example: true
ip:
description: Peer's IP address
type: string
format: ipv4
example: 100.64.0.15
ipv6:
description: Peer's IPv6 overlay address. Omitted if IPv6 is not enabled for the account.
type: string
format: ipv6
example: "fd00:4e42:ab12::1"
required:
- name
- ssh_enabled
- login_expiration_enabled
- inactivity_expiration_enabled
PeerMinimum:
type: object
properties:
id:
description: Peer ID
type: string
example: chacbco6lnnbn6cg5s90
name:
description: Peer's hostname
type: string
example: stage-host-1
required:
- id
- name
Peer:
allOf:
- $ref: '#/components/schemas/PeerMinimum'
- type: object
properties:
created_at:
description: Peer creation date (UTC)
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
ip:
description: Peer's IP address
type: string
example: 10.64.0.1
ipv6:
description: Peer's IPv6 overlay address
type: string
format: ipv6
example: "fd00:4e42:ab12::1"
connection_ip:
description: Peer's public connection IP address
type: string
example: 35.64.0.1
connected:
description: Peer to Management connection status
type: boolean
example: true
last_seen:
description: Last time peer connected to Netbird's management service
type: string
format: date-time
example: "2023-05-05T10:05:26.420578Z"
os:
description: Peer's operating system and version
type: string
example: Darwin 13.2.1
kernel_version:
description: Peer's operating system kernel version
type: string
example: 23.2.0
geoname_id:
description: Unique identifier from the GeoNames database for a specific geographical location.
type: integer
example: 2643743
version:
description: Peer's daemon or cli version
type: string
example: 0.14.0
groups:
description: Groups that the peer belongs to
type: array
items:
$ref: '../group/components.yaml#/components/schemas/GroupMinimum'
ssh_enabled:
description: Indicates whether SSH server is enabled on this peer
type: boolean
example: true
user_id:
description: User ID of the user that enrolled this peer
type: string
example: google-oauth2|277474792786460067937
hostname:
description: Hostname of the machine
type: string
example: stage-host-1
ui_version:
description: Peer's desktop UI version
type: string
example: 0.14.0
dns_label:
description: Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
type: string
example: stage-host-1.netbird.cloud
login_expiration_enabled:
description: Indicates whether peer login expiration has been enabled or not
type: boolean
example: false
login_expired:
description: Indicates whether peer's login expired or not
type: boolean
example: false
last_login:
description: Last time this peer performed log in (authentication). E.g., user authenticated.
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
inactivity_expiration_enabled:
description: Indicates whether peer inactivity expiration has been enabled or not
type: boolean
example: false
approval_required:
description: (Cloud only) Indicates whether peer needs approval
type: boolean
example: true
disapproval_reason:
description: (Cloud only) Reason why the peer requires approval
type: string
country_code:
$ref: '../openapi.yaml#/components/schemas/CountryCode'
city_name:
$ref: '../openapi.yaml#/components/schemas/CityName'
serial_number:
description: System serial number
type: string
example: "C02XJ0J0JGH7"
extra_dns_labels:
description: Extra DNS labels added to the peer
type: array
items:
type: string
example: "stage-host-1"
ephemeral:
description: Indicates whether the peer is ephemeral or not
type: boolean
example: false
local_flags:
$ref: '#/components/schemas/PeerLocalFlags'
required:
- city_name
- connected
- connection_ip
- country_code
- created_at
- dns_label
- geoname_id
- groups
- hostname
- ip
- kernel_version
- last_login
- last_seen
- login_expiration_enabled
- login_expired
- inactivity_expiration_enabled
- os
- ssh_enabled
- user_id
- version
- ui_version
- approval_required
- serial_number
- extra_dns_labels
- ephemeral
PeerLocalFlags:
type: object
properties:
rosenpass_enabled:
description: Indicates whether Rosenpass is enabled on this peer
type: boolean
example: true
rosenpass_permissive:
description: Indicates whether Rosenpass is in permissive mode or not
type: boolean
example: false
server_ssh_allowed:
description: Indicates whether SSH access this peer is allowed or not
type: boolean
example: true
remote_jobs_allowed:
description: Indicates whether the peer has opted into management-requested remote jobs (e.g. debug bundles)
type: boolean
example: true
disable_client_routes:
description: Indicates whether client routes are disabled on this peer or not
type: boolean
example: false
disable_server_routes:
description: Indicates whether server routes are disabled on this peer or not
type: boolean
example: false
disable_dns:
description: Indicates whether DNS management is disabled on this peer or not
type: boolean
example: false
disable_firewall:
description: Indicates whether firewall management is disabled on this peer or not
type: boolean
example: false
block_lan_access:
description: Indicates whether LAN access is blocked on this peer when used as a routing peer
type: boolean
example: false
block_inbound:
description: Indicates whether inbound traffic is blocked on this peer
type: boolean
example: false
lazy_connection_enabled:
description: Indicates whether lazy connection is enabled on this peer
type: boolean
example: false
PeerBatch:
allOf:
- $ref: '#/components/schemas/Peer'
- type: object
properties:
created_at:
description: Peer creation date (UTC)
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
accessible_peers_count:
description: Number of accessible peers
type: integer
example: 5
required:
- created_at
- accessible_peers_count
@@ -0,0 +1,93 @@
get:
summary: Retrieve a Peer
description: Get information about a peer
tags: [ Peers ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
responses:
'200':
description: A Peer object
content:
application/json:
schema:
$ref: './components.yaml#/components/schemas/Peer'
'400':
"$ref": '../openapi.yaml#/components/responses/bad_request'
'401':
"$ref": '../openapi.yaml#/components/responses/requires_authentication'
'403':
"$ref": '../openapi.yaml#/components/responses/forbidden'
'500':
"$ref": '../openapi.yaml#/components/responses/internal_error'
put:
summary: Update a Peer
description: Update information about a peer
tags: [ Peers ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
requestBody:
description: update a peer
required: true
content:
'application/json':
schema:
$ref: './components.yaml#/components/schemas/PeerRequest'
responses:
'200':
description: A Peer object
content:
application/json:
schema:
$ref: './components.yaml#/components/schemas/Peer'
'400':
"$ref": '../openapi.yaml#/components/responses/bad_request'
'401':
"$ref": '../openapi.yaml#/components/responses/requires_authentication'
'403':
"$ref": '../openapi.yaml#/components/responses/forbidden'
'422':
"$ref": "../openapi.yaml#/components/responses/unprocessable"
'500':
"$ref": '../openapi.yaml#/components/responses/internal_error'
delete:
summary: Delete a Peer
description: Delete a peer
tags: [ Peers ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
responses:
'200':
description: Delete status code
content: { }
'400':
"$ref": '../openapi.yaml#/components/responses/bad_request'
'401':
"$ref": '../openapi.yaml#/components/responses/requires_authentication'
'403':
"$ref": '../openapi.yaml#/components/responses/forbidden'
'500':
"$ref": '../openapi.yaml#/components/responses/internal_error'
@@ -0,0 +1,77 @@
get:
summary: List all Peers
description: Returns a list of all peers
tags: [ Peers ]
parameters:
- name: page
in: query
description: Page number
required: false
schema:
type: integer
minimum: 1
default: 1
- name: page_size
in: query
description: Number of peers per page
required: false
schema:
type: integer
minimum: 1
maximum: 250
default: 100
- name: connected
in: query
description: Filter by peer connected status
required: false
schema:
type: boolean
- name: approval_required
in: query
description: Filter by approval_required field
required: false
schema:
type: boolean
- name: os
in: query
description: Peer os
required: false
schema:
type: array
items:
type: string
enum:
- linux
- windows
- mac
- android
- ios
- js
- name: search
in: query
description: filter peers by name, dns_label, ip, os, version, serial_number, owner name, owner email, group names, mac
required: false
schema:
type: string
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
responses:
'200':
description: A JSON Array of Peers
content:
application/json:
schema:
type: array
items:
$ref: './components.yaml#/components/schemas/PeerBatch'
'400':
"$ref": "../openapi.yaml#/components/responses/bad_request"
'401':
"$ref": "../openapi.yaml#/components/responses/requires_authentication"
'403':
"$ref": "../openapi.yaml#/components/responses/forbidden"
'422':
"$ref": "../openapi.yaml#/components/responses/unprocessable"
'500':
"$ref": "../openapi.yaml#/components/responses/internal_error"
@@ -0,0 +1,93 @@
package apiv1alpha1
import (
_ "embed"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"github.com/netbirdio/netbird/shared/management/http/util"
"github.com/netbirdio/netbird/shared/management/status"
"github.com/pb33f/libopenapi"
validator "github.com/pb33f/libopenapi-validator"
"github.com/pb33f/libopenapi-validator/config"
"github.com/pb33f/libopenapi-validator/errors"
"github.com/pb33f/libopenapi/datamodel"
log "github.com/sirupsen/logrus"
)
// TODO (dmitri) this needs to be extracted, as it will grow to 500Kb
//
//go:embed bundle.yaml
var bundle []byte
func CreateV1ApiValidatingMiddleware() (*V1ValidatorMiddleware, error) {
doc, err := libopenapi.NewDocumentWithConfiguration(bundle, &datamodel.DocumentConfiguration{
Logger: slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})),
})
if err != nil {
return nil, err
}
model, err := doc.BuildV3Model()
if err != nil {
return nil, err
}
// TODO figure out document validation: rn it's possible to have a spec
// that's not entirely correct -- parts of it fail to parse, but silently
v := validator.NewValidatorFromV3Model(&model.Model,
config.WithoutSecurityValidation(),
config.WithStandardBodyDecoders(),
config.WithRejectUnsupportedBodyContent(),
config.WithRequestDefaults())
// v.SetDocument(doc)
// v.ValidatePathParams()
// if valid, errs := v.ValidateDocument(); !valid {
// return nil, fmt.Errorf("error validating OpenAPI doc, %s", errs)
// }
return &V1ValidatorMiddleware{Validator: v}, nil
}
type V1ValidatorMiddleware struct {
Validator validator.Validator
}
func (v *V1ValidatorMiddleware) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
valid, errs := v.Validator.ValidateHttpRequestSync(r)
if !valid {
validationErrs := make([]string, 0, len(errs))
for _, err := range errs {
validationErrs = append(validationErrs, validationError(err))
}
log.WithContext(r.Context()).Debugf("error validating request: %s", strings.Join(validationErrs, ", "))
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid request: %s", strings.Join(validationErrs, ", ")), w)
return
}
h.ServeHTTP(w, r)
})
}
func validationError(err *errors.ValidationError) string {
if err.SchemaValidationErrors != nil {
errs := make([]string, 0, len(err.SchemaValidationErrors))
for _, e := range err.SchemaValidationErrors {
errs = append(errs, fmt.Sprintf("field %s: %s", e.FieldPath, e.Reason))
}
return fmt.Sprintf("%s: %s", err.Message, strings.Join(errs, ", "))
} else {
if err.SpecLine > 0 && err.SpecCol > 0 {
return fmt.Sprintf("%s, Line: %d, Column: %d", err.Message, err.SpecLine, err.SpecCol)
} else {
return fmt.Sprint(err.Message)
}
}
}
@@ -0,0 +1,152 @@
package apiv1alpha1
import (
"log/slog"
"os"
"path/filepath"
"strings"
"unicode"
"github.com/pb33f/libopenapi"
"github.com/pb33f/libopenapi/bundler"
"github.com/pb33f/libopenapi/datamodel"
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
"github.com/pb33f/libopenapi/generator/golang"
)
var ApiPath = filepath.Join("shared", "management", "http", "apiv1alpha1", "openapi.yaml")
func GenerateV1ApiBindings(openapipath string) ([]byte, *v3.Document, error) {
specFile, err := os.ReadFile(openapipath)
if err != nil {
return nil, nil, err
}
multiFileDoc, err := libopenapi.NewDocumentWithConfiguration(specFile, &datamodel.DocumentConfiguration{
AllowFileReferences: true,
BasePath: filepath.Dir(openapipath),
SpecFilePath: openapipath,
ExtractRefsSequentially: true,
Logger: slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelError,
})),
TransformSiblingRefs: true, // enable openapi 3.1 compliance by default
MergeReferencedProperties: true, // enable enhanced resolution by default
PropertyMergeStrategy: datamodel.PreserveLocal, // local properties take precedence
})
if err != nil {
return nil, nil, err
}
multiFileModel, err := multiFileDoc.BuildV3Model()
if err != nil {
return nil, nil, err
}
bundle, err := bundler.BundleDocumentComposed(&multiFileModel.Model, &bundler.BundleCompositionConfig{
StrictValidation: true,
})
if err != nil {
return nil, nil, err
}
doc, err := libopenapi.NewDocumentWithConfiguration(bundle, &datamodel.DocumentConfiguration{
Logger: slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})),
})
if err != nil {
return nil, nil, err
}
model, err := doc.BuildV3Model()
if err != nil {
return nil, nil, err
}
return bundle, &model.Model, nil
}
func GenerateV1Schema(model *v3.Document) (*golang.GeneratedFile, error) {
// render every schema in components.schemas into one file
gen := golang.NewGenerator(
golang.WithGeneratedComment(true),
golang.WithFormatMapping("date-time", "time.Time", "time"),
golang.WithOptionalFieldsAsPointers(true),
golang.WithEnumConstants(true),
golang.WithNestedTypeNameDelimiter(""),
golang.WithPackageName("apiv1alpha1"),
golang.WithFieldNameResolver(toPublicName))
return gen.RenderSchemas(model.Components.Schemas)
}
// this is to keep existing naming of fields like "id", "url", etc
// libopenapi by default converts them to all-uppercase, like "ID", "URL", etc
func toPublicName(name string) string {
parts := splitIdentifier(name)
if len(parts) == 0 {
return "Value"
}
var b strings.Builder
for _, p := range parts {
rs := []rune(strings.ToLower(p))
rs[0] = unicode.ToUpper(rs[0])
b.WriteString(string(rs))
}
out := b.String()
first := []rune(out)[0]
if unicode.IsDigit(first) {
return "Value" + out
}
return out
}
func splitIdentifier(name string) []string {
var raw []string
var b strings.Builder
flush := func() {
if b.Len() > 0 {
raw = append(raw, b.String())
b.Reset()
}
}
for _, r := range name {
switch {
case unicode.IsLetter(r) || unicode.IsDigit(r):
b.WriteRune(r)
default:
flush()
}
}
flush()
var parts []string
for _, part := range raw {
parts = append(parts, splitCamel(part)...)
}
return parts
}
func splitCamel(value string) []string {
rs := []rune(value)
if len(rs) == 0 {
return nil
}
var parts []string
start := 0
for i := 1; i < len(rs); i++ {
prev := rs[i-1]
cur := rs[i]
var next rune
if i+1 < len(rs) {
next = rs[i+1]
}
lowerToUpper := unicode.IsLower(prev) && unicode.IsUpper(cur)
acronymToWord := unicode.IsUpper(prev) && unicode.IsUpper(cur) && next != 0 && unicode.IsLower(next)
if lowerToUpper || acronymToWord {
parts = append(parts, string(rs[start:i]))
start = i
}
}
parts = append(parts, string(rs[start:]))
return parts
}
@@ -0,0 +1,414 @@
// Code generated by libopenapi generator/golang. DO NOT EDIT.
package apiv1alpha1
import (
"encoding/json"
"time"
)
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country.
// CountryCode example value is defined in the OpenAPI schema.
type CountryCode string
// CityName Commonly used English name of the city.
// CityName example value is defined in the OpenAPI schema.
type CityName string
// Country Describe country geographical location information.
type Country struct {
// CountryName Commonly used English name of the country.
// CountryName example value is defined in the OpenAPI schema.
CountryName string `json:"country_name"`
CountryCode CountryCode `json:"country_code"`
}
// City Describe city geographical location information.
type City struct {
// GeonameId Integer ID of the record in GeoNames database.
// GeonameId example value is defined in the OpenAPI schema.
GeonameId int `json:"geoname_id"`
// CityName Commonly used English name of the city.
// CityName example value is defined in the OpenAPI schema.
CityName string `json:"city_name"`
}
// ErrorResponse Standard error response.
type ErrorResponse struct {
// Message A human-readable error message.
// Message example value is defined in the OpenAPI schema.
Message *string `json:"message,omitempty"`
}
type PeerBatch struct {
Peer
// CreatedAt Peer creation date (UTC).
// CreatedAt example value is defined in the OpenAPI schema.
CreatedAt time.Time `json:"created_at"`
// AccessiblePeersCount Number of accessible peers.
// AccessiblePeersCount example value is defined in the OpenAPI schema.
AccessiblePeersCount int `json:"accessible_peers_count"`
}
type PeerMinimum struct {
// Id Peer ID.
// Id example value is defined in the OpenAPI schema.
Id string `json:"id"`
// Name Peer's hostname.
// Name example value is defined in the OpenAPI schema.
Name string `json:"name"`
}
// GroupMinimumIssued How the group was issued (api, integration, jwt).
// GroupMinimumIssued example value is defined in the OpenAPI schema.
type GroupMinimumIssued string
const (
GroupMinimumIssuedAPI GroupMinimumIssued = "api"
GroupMinimumIssuedIntegration GroupMinimumIssued = "integration"
GroupMinimumIssuedJWT GroupMinimumIssued = "jwt"
)
type GroupMinimum struct {
// Id Group ID.
// Id example value is defined in the OpenAPI schema.
Id string `json:"id"`
// Name Group Name identifier.
// Name example value is defined in the OpenAPI schema.
Name string `json:"name"`
// PeersCount Count of peers associated to the group.
// PeersCount example value is defined in the OpenAPI schema.
PeersCount int `json:"peers_count"`
// ResourcesCount Count of resources associated to the group.
// ResourcesCount example value is defined in the OpenAPI schema.
ResourcesCount int `json:"resources_count"`
// Issued How the group was issued (api, integration, jwt).
// Issued example value is defined in the OpenAPI schema.
Issued *GroupMinimumIssued `json:"issued,omitempty"`
}
type PeerLocalFlags struct {
// RosenpassEnabled Indicates whether Rosenpass is enabled on this peer.
// RosenpassEnabled example value is defined in the OpenAPI schema.
RosenpassEnabled *bool `json:"rosenpass_enabled,omitempty"`
// RosenpassPermissive Indicates whether Rosenpass is in permissive mode or not.
// RosenpassPermissive example value is defined in the OpenAPI schema.
RosenpassPermissive *bool `json:"rosenpass_permissive,omitempty"`
// ServerSshAllowed Indicates whether SSH access this peer is allowed or not.
// ServerSshAllowed example value is defined in the OpenAPI schema.
ServerSshAllowed *bool `json:"server_ssh_allowed,omitempty"`
// RemoteJobsAllowed Indicates whether the peer has opted into management-requested remote jobs (e.g. debug bundles).
// RemoteJobsAllowed example value is defined in the OpenAPI schema.
RemoteJobsAllowed *bool `json:"remote_jobs_allowed,omitempty"`
// DisableClientRoutes Indicates whether client routes are disabled on this peer or not.
// DisableClientRoutes example value is defined in the OpenAPI schema.
DisableClientRoutes *bool `json:"disable_client_routes,omitempty"`
// DisableServerRoutes Indicates whether server routes are disabled on this peer or not.
// DisableServerRoutes example value is defined in the OpenAPI schema.
DisableServerRoutes *bool `json:"disable_server_routes,omitempty"`
// DisableDns Indicates whether DNS management is disabled on this peer or not.
// DisableDns example value is defined in the OpenAPI schema.
DisableDns *bool `json:"disable_dns,omitempty"`
// DisableFirewall Indicates whether firewall management is disabled on this peer or not.
// DisableFirewall example value is defined in the OpenAPI schema.
DisableFirewall *bool `json:"disable_firewall,omitempty"`
// BlockLanAccess Indicates whether LAN access is blocked on this peer when used as a routing peer.
// BlockLanAccess example value is defined in the OpenAPI schema.
BlockLanAccess *bool `json:"block_lan_access,omitempty"`
// BlockInbound Indicates whether inbound traffic is blocked on this peer.
// BlockInbound example value is defined in the OpenAPI schema.
BlockInbound *bool `json:"block_inbound,omitempty"`
// LazyConnectionEnabled Indicates whether lazy connection is enabled on this peer.
// LazyConnectionEnabled example value is defined in the OpenAPI schema.
LazyConnectionEnabled *bool `json:"lazy_connection_enabled,omitempty"`
}
type Peer struct {
PeerMinimum
// CreatedAt Peer creation date (UTC).
// CreatedAt example value is defined in the OpenAPI schema.
CreatedAt time.Time `json:"created_at"`
// Ip Peer's IP address.
// Ip example value is defined in the OpenAPI schema.
Ip string `json:"ip"`
// Ipv6 Peer's IPv6 overlay address.
// Ipv6 example value is defined in the OpenAPI schema.
Ipv6 *string `json:"ipv6,omitempty"`
// ConnectionIp Peer's public connection IP address.
// ConnectionIp example value is defined in the OpenAPI schema.
ConnectionIp string `json:"connection_ip"`
// Connected Peer to Management connection status.
// Connected example value is defined in the OpenAPI schema.
Connected bool `json:"connected"`
// LastSeen Last time peer connected to Netbird's management service.
// LastSeen example value is defined in the OpenAPI schema.
LastSeen time.Time `json:"last_seen"`
// Os Peer's operating system and version.
// Os example value is defined in the OpenAPI schema.
Os string `json:"os"`
// KernelVersion Peer's operating system kernel version.
// KernelVersion example value is defined in the OpenAPI schema.
KernelVersion string `json:"kernel_version"`
// GeonameId Unique identifier from the GeoNames database for a specific geographical location.
// GeonameId example value is defined in the OpenAPI schema.
GeonameId int `json:"geoname_id"`
// Version Peer's daemon or cli version.
// Version example value is defined in the OpenAPI schema.
Version string `json:"version"`
// Groups Groups that the peer belongs to.
Groups []GroupMinimum `json:"groups"`
// SshEnabled Indicates whether SSH server is enabled on this peer.
// SshEnabled example value is defined in the OpenAPI schema.
SshEnabled bool `json:"ssh_enabled"`
// UserId User ID of the user that enrolled this peer.
// UserId example value is defined in the OpenAPI schema.
UserId string `json:"user_id"`
// Hostname Hostname of the machine.
// Hostname example value is defined in the OpenAPI schema.
Hostname string `json:"hostname"`
// UiVersion Peer's desktop UI version.
// UiVersion example value is defined in the OpenAPI schema.
UiVersion string `json:"ui_version"`
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud.
// DnsLabel example value is defined in the OpenAPI schema.
DnsLabel string `json:"dns_label"`
// LoginExpirationEnabled Indicates whether peer login expiration has been enabled or not.
// LoginExpirationEnabled example value is defined in the OpenAPI schema.
LoginExpirationEnabled bool `json:"login_expiration_enabled"`
// LoginExpired Indicates whether peer's login expired or not.
// LoginExpired example value is defined in the OpenAPI schema.
LoginExpired bool `json:"login_expired"`
// LastLogin Last time this peer performed log in (authentication). E.g., user authenticated.
// LastLogin example value is defined in the OpenAPI schema.
LastLogin time.Time `json:"last_login"`
// InactivityExpirationEnabled Indicates whether peer inactivity expiration has been enabled or not.
// InactivityExpirationEnabled example value is defined in the OpenAPI schema.
InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"`
// ApprovalRequired (Cloud only) Indicates whether peer needs approval.
// ApprovalRequired example value is defined in the OpenAPI schema.
ApprovalRequired bool `json:"approval_required"`
// DisapprovalReason (Cloud only) Reason why the peer requires approval.
DisapprovalReason *string `json:"disapproval_reason,omitempty"`
CountryCode CountryCode `json:"country_code"`
CityName CityName `json:"city_name"`
// SerialNumber System serial number.
// SerialNumber example value is defined in the OpenAPI schema.
SerialNumber string `json:"serial_number"`
// ExtraDnsLabels Extra DNS labels added to the peer.
ExtraDnsLabels []string `json:"extra_dns_labels"`
// Ephemeral Indicates whether the peer is ephemeral or not.
// Ephemeral example value is defined in the OpenAPI schema.
Ephemeral bool `json:"ephemeral"`
LocalFlags *PeerLocalFlags `json:"local_flags,omitempty"`
}
type PeerRequest struct {
// Name example value is defined in the OpenAPI schema.
Name string `json:"name"`
// SshEnabled example value is defined in the OpenAPI schema.
SshEnabled bool `json:"ssh_enabled"`
// LoginExpirationEnabled example value is defined in the OpenAPI schema.
LoginExpirationEnabled bool `json:"login_expiration_enabled"`
// InactivityExpirationEnabled example value is defined in the OpenAPI schema.
InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"`
// ApprovalRequired (Cloud only) Indicates whether peer needs approval.
// ApprovalRequired example value is defined in the OpenAPI schema.
ApprovalRequired *bool `json:"approval_required,omitempty"`
// Ip Peer's IP address.
// Ip example value is defined in the OpenAPI schema.
Ip *string `json:"ip,omitempty"`
// Ipv6 Peer's IPv6 overlay address. Omitted if IPv6 is not enabled for the account.
// Ipv6 example value is defined in the OpenAPI schema.
Ipv6 *string `json:"ipv6,omitempty"`
}
// UserStatus User's status.
// UserStatus example value is defined in the OpenAPI schema.
type UserStatus string
const (
UserStatusActive UserStatus = "active"
UserStatusInvited UserStatus = "invited"
UserStatusBlocked UserStatus = "blocked"
)
type User struct {
// Id User ID.
// Id example value is defined in the OpenAPI schema.
Id string `json:"id"`
// Email User's email address.
// Email example value is defined in the OpenAPI schema.
Email string `json:"email"`
// Password User's password. Only present when user is created (create user endpoint is called) and only when IdP supports user creation with password.
// Password example value is defined in the OpenAPI schema.
Password *string `json:"password,omitempty"`
// Name User's name from idp provider.
// Name example value is defined in the OpenAPI schema.
Name string `json:"name"`
// Role User's NetBird account role.
// Role example value is defined in the OpenAPI schema.
Role string `json:"role"`
// Status User's status.
// Status example value is defined in the OpenAPI schema.
Status UserStatus `json:"status"`
// LastLogin Last time this user performed a login to the dashboard.
// LastLogin example value is defined in the OpenAPI schema.
LastLogin *time.Time `json:"last_login,omitempty"`
// AutoGroups Group IDs to auto-assign to peers registered by this user.
AutoGroups []string `json:"auto_groups"`
// IsCurrent Is true if authenticated user is the same as this user.
// IsCurrent readOnly.
// IsCurrent example value is defined in the OpenAPI schema.
IsCurrent *bool `json:"is_current,omitempty"`
// IsServiceUser Is true if this user is a service user.
// IsServiceUser readOnly.
// IsServiceUser example value is defined in the OpenAPI schema.
IsServiceUser *bool `json:"is_service_user,omitempty"`
// IsBlocked Is true if this user is blocked. Blocked users can't use the system.
// IsBlocked example value is defined in the OpenAPI schema.
IsBlocked bool `json:"is_blocked"`
// PendingApproval Is true if this user requires approval before being activated. Only applicable for users joining via domain matching when user_approval_required is enabled.
// PendingApproval example value is defined in the OpenAPI schema.
PendingApproval bool `json:"pending_approval"`
// Issued How user was issued by API or Integration.
// Issued example value is defined in the OpenAPI schema.
Issued *string `json:"issued,omitempty"`
// IdpId Identity provider ID (connector ID) that the user authenticated with. Only populated for users with Dex-encoded user IDs.
// IdpId example value is defined in the OpenAPI schema.
IdpId *string `json:"idp_id,omitempty"`
Permissions *UserPermissions `json:"permissions,omitempty"`
}
type UserCreateRequest struct {
// Email User's Email to send invite to.
// Email example value is defined in the OpenAPI schema.
Email *string `json:"email,omitempty"`
// Name User's full name.
// Name example value is defined in the OpenAPI schema.
Name *string `json:"name,omitempty"`
// Role User's NetBird account role.
// Role example value is defined in the OpenAPI schema.
Role string `json:"role"`
// AutoGroups Group IDs to auto-assign to peers registered by this user.
AutoGroups []string `json:"auto_groups"`
// IsServiceUser Is true if this user is a service user.
// IsServiceUser example value is defined in the OpenAPI schema.
IsServiceUser bool `json:"is_service_user"`
}
type UserRequest struct {
// Role User's NetBird account role.
// Role example value is defined in the OpenAPI schema.
Role string `json:"role"`
// AutoGroups Group IDs to auto-assign to peers registered by this user.
AutoGroups []string `json:"auto_groups"`
// IsBlocked If set to true then user is blocked and can't use the system.
// IsBlocked example value is defined in the OpenAPI schema.
IsBlocked bool `json:"is_blocked"`
}
type UserPermissionsModulesAdditionalProperty struct {
AdditionalProperties map[string]bool `json:"-"`
}
func (m *UserPermissionsModulesAdditionalProperty) UnmarshalJSON(data []byte) error {
type Alias UserPermissionsModulesAdditionalProperty
var known Alias
if err := json.Unmarshal(data, &known); err != nil {
return err
}
*m = UserPermissionsModulesAdditionalProperty(known)
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if len(raw) == 0 {
return nil
}
m.AdditionalProperties = make(map[string]bool, len(raw))
for key, value := range raw {
var decoded bool
if err := json.Unmarshal(value, &decoded); err != nil {
return err
}
m.AdditionalProperties[key] = decoded
}
return nil
}
func (m UserPermissionsModulesAdditionalProperty) MarshalJSON() ([]byte, error) {
type Alias UserPermissionsModulesAdditionalProperty
encoded, err := json.Marshal(Alias(m))
if err != nil {
return nil, err
}
var object map[string]json.RawMessage
if err := json.Unmarshal(encoded, &object); err != nil {
return nil, err
}
for key, value := range m.AdditionalProperties {
encodedValue, err := json.Marshal(value)
if err != nil {
return nil, err
}
object[key] = encodedValue
}
return json.Marshal(object)
}
// UserPermissionsModules example value is defined in the OpenAPI schema.
type UserPermissionsModules struct {
AdditionalProperties map[string]map[string]bool `json:"-"`
}
func (m *UserPermissionsModules) UnmarshalJSON(data []byte) error {
type Alias UserPermissionsModules
var known Alias
if err := json.Unmarshal(data, &known); err != nil {
return err
}
*m = UserPermissionsModules(known)
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if len(raw) == 0 {
return nil
}
m.AdditionalProperties = make(map[string]map[string]bool, len(raw))
for key, value := range raw {
var decoded map[string]bool
if err := json.Unmarshal(value, &decoded); err != nil {
return err
}
m.AdditionalProperties[key] = decoded
}
return nil
}
func (m UserPermissionsModules) MarshalJSON() ([]byte, error) {
type Alias UserPermissionsModules
encoded, err := json.Marshal(Alias(m))
if err != nil {
return nil, err
}
var object map[string]json.RawMessage
if err := json.Unmarshal(encoded, &object); err != nil {
return nil, err
}
for key, value := range m.AdditionalProperties {
encodedValue, err := json.Marshal(value)
if err != nil {
return nil, err
}
object[key] = encodedValue
}
return json.Marshal(object)
}
type UserPermissions struct {
// IsRestricted Indicates whether this User's Peers view is restricted.
IsRestricted bool `json:"is_restricted"`
// Modules example value is defined in the OpenAPI schema.
Modules map[string]map[string]bool `json:"modules"`
}
@@ -0,0 +1,150 @@
components:
schemas:
User:
type: object
properties:
id:
description: User ID
type: string
example: google-oauth2|277474792786460067937
email:
description: User's email address
type: string
example: demo@netbird.io
password:
description: User's password. Only present when user is created (create user endpoint is called) and only when IdP supports user creation with password.
type: string
example: super_secure_password
name:
description: User's name from idp provider
type: string
example: Tom Schulz
role:
description: User's NetBird account role
type: string
example: admin
status:
description: User's status
type: string
enum: [ "active", "invited", "blocked" ]
example: active
last_login:
description: Last time this user performed a login to the dashboard
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
auto_groups:
description: Group IDs to auto-assign to peers registered by this user
type: array
items:
type: string
example: ch8i4ug6lnn4g9hqv7m0
is_current:
description: Is true if authenticated user is the same as this user
type: boolean
readOnly: true
example: true
is_service_user:
description: Is true if this user is a service user
type: boolean
readOnly: true
example: false
is_blocked:
description: Is true if this user is blocked. Blocked users can't use the system
type: boolean
example: false
pending_approval:
description: Is true if this user requires approval before being activated. Only applicable for users joining via domain matching when user_approval_required is enabled.
type: boolean
example: false
issued:
description: How user was issued by API or Integration
type: string
example: api
idp_id:
description: Identity provider ID (connector ID) that the user authenticated with. Only populated for users with Dex-encoded user IDs.
type: string
example: okta-abc123
permissions:
$ref: '#/components/schemas/UserPermissions'
required:
- id
- email
- name
- role
- auto_groups
- status
- is_blocked
- pending_approval
UserPermissions:
type: object
properties:
is_restricted:
type: boolean
description: Indicates whether this User's Peers view is restricted
modules:
type: object
additionalProperties:
type: object
additionalProperties:
type: boolean
propertyNames:
type: string
description: The operation type
propertyNames:
type: string
description: The module name
example: { "networks": { "read": true, "create": false, "update": false, "delete": false }, "peers": { "read": false, "create": false, "update": false, "delete": false } }
required:
- modules
- is_restricted
UserRequest:
type: object
properties:
role:
description: User's NetBird account role
type: string
example: admin
auto_groups:
description: Group IDs to auto-assign to peers registered by this user
type: array
items:
type: string
example: ch8i4ug6lnn4g9hqv7m0
is_blocked:
description: If set to true then user is blocked and can't use the system
type: boolean
example: false
required:
- role
- auto_groups
- is_blocked
UserCreateRequest:
type: object
properties:
email:
description: User's Email to send invite to
type: string
example: demo@netbird.io
name:
description: User's full name
type: string
example: Tom Schulz
role:
description: User's NetBird account role
type: string
example: admin
auto_groups:
description: Group IDs to auto-assign to peers registered by this user
type: array
items:
type: string
example: ch8i4ug6lnn4g9hqv7m0
is_service_user:
description: Is true if this user is a service user
type: boolean
example: false
required:
- role
- auto_groups
- is_service_user
@@ -0,0 +1,129 @@
UsersPath:
get:
summary: List all Users
description: Returns a list of all users
tags: [ Users ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: query
name: service_user
schema:
type: boolean
description: Filters users and returns either regular users or service users
responses:
'200':
description: A JSON array of Users
content:
application/json:
schema:
type: array
items:
$ref: './user_components.yaml#/components/schemas/User'
'400':
"$ref": "../openapi.yaml#/components/responses/bad_request"
'401':
"$ref": "../openapi.yaml#/components/responses/requires_authentication"
'403':
"$ref": "../openapi.yaml#/components/responses/forbidden"
'422':
"$ref": "../openapi.yaml#/components/responses/unprocessable"
'500':
"$ref": "../openapi.yaml#/components/responses/internal_error"
post:
summary: Create a User
description: Creates a new service user or sends an invite to a regular user
tags: [ Users ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
requestBody:
description: User invite information
required: true
content:
'application/json':
schema:
$ref: './user_components.yaml#/components/schemas/UserCreateRequest'
responses:
'200':
description: A User object
content:
application/json:
schema:
$ref: './user_components.yaml#/components/schemas/User'
'400':
"$ref": "../openapi.yaml#/components/responses/bad_request"
'401':
"$ref": "../openapi.yaml#/components/responses/requires_authentication"
'403':
"$ref": "../openapi.yaml#/components/responses/forbidden"
'422':
"$ref": "../openapi.yaml#/components/responses/unprocessable"
'500':
"$ref": "../openapi.yaml#/components/responses/internal_error"
UserPath:
put:
summary: Update a User
description: Update information about a User
tags: [ Users ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: userId
required: true
schema:
type: string
description: The unique identifier of a user
requestBody:
description: User update
required: true
content:
'application/json':
schema:
$ref: './user_components.yaml#/components/schemas/UserRequest'
responses:
'200':
description: A User object
content:
application/json:
schema:
$ref: './user_components.yaml#/components/schemas/User'
'400':
"$ref": "../openapi.yaml#/components/responses/bad_request"
'401':
"$ref": "../openapi.yaml#/components/responses/requires_authentication"
'403':
"$ref": "../openapi.yaml#/components/responses/forbidden"
'422':
"$ref": "../openapi.yaml#/components/responses/unprocessable"
'500':
"$ref": "../openapi.yaml#/components/responses/internal_error"
delete:
summary: Delete a User
description: This method removes a user from accessing the system. For this leaves the IDP user intact unless the `--user-delete-from-idp` is passed to management startup.
tags: [ Users ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: userId
required: true
schema:
type: string
description: The unique identifier of a user
responses:
'200':
description: Delete status code
content: { }
'400':
"$ref": "../openapi.yaml#/components/responses/bad_request"
'401':
"$ref": "../openapi.yaml#/components/responses/requires_authentication"
'403':
"$ref": "../openapi.yaml#/components/responses/forbidden"
'500':
"$ref": "../openapi.yaml#/components/responses/internal_error"
+3 -65
View File
@@ -15,7 +15,7 @@ import (
// - NetworkMap is the *proto.NetworkMap shape the engine reads today via
// update.GetNetworkMap() — built from the envelope's components by
// running Calculate() locally + converting back through the shared
// proto helpers + merging the optional ProxyPatch.
// proto helpers.
// - Components is the *types.NetworkMapComponents the engine retains so
// future incremental delta updates have a base to apply changes
// against. The client keeps it under its sync lock.
@@ -26,8 +26,8 @@ type EnvelopeResult struct {
// EnvelopeToNetworkMap is the full client-side pipeline: decode the
// component envelope back to a typed NetworkMapComponents, run Calculate()
// locally to produce the typed NetworkMap, convert it to the wire form the
// engine consumes, and fold in any ProxyPatch the server attached.
// locally to produce the typed NetworkMap and convert it to the wire form the
// engine consumes.
//
// localPeerKey is the receiving peer's WG pub key (used to derive
// includeIPv6 / useSourcePrefixes from the receiving peer's own record in
@@ -107,74 +107,12 @@ func EnvelopeToNetworkMap(ctx context.Context, env *proto.NetworkMapEnvelope, lo
}
}
if typedNM.ForwardingRules != nil {
forwardingRules := make([]*proto.ForwardingRule, 0, len(typedNM.ForwardingRules))
for _, rule := range typedNM.ForwardingRules {
forwardingRules = append(forwardingRules, rule.ToProto())
}
protoNM.ForwardingRules = forwardingRules
}
// Merge the proxy patch the server attached. Mirrors the legacy
// NetworkMap.Merge step that the server runs after Calculate().
if full != nil && full.ProxyPatch != nil {
mergeProxyPatch(protoNM, full.ProxyPatch)
}
return &EnvelopeResult{
NetworkMap: protoNM,
Components: components,
}, nil
}
// mergeProxyPatch folds a ProxyPatch's pre-expanded fragments into the
// proto.NetworkMap that Calculate() produced. Mirrors types.NetworkMap.Merge
// — same six collections, deduplicated where the legacy merge dedupes.
func mergeProxyPatch(nm *proto.NetworkMap, patch *proto.ProxyPatch) {
nm.RemotePeers = appendUniquePeers(nm.RemotePeers, patch.Peers)
nm.OfflinePeers = appendUniquePeers(nm.OfflinePeers, patch.OfflinePeers)
nm.FirewallRules = append(nm.FirewallRules, patch.FirewallRules...)
nm.Routes = append(nm.Routes, patch.Routes...)
nm.RoutesFirewallRules = append(nm.RoutesFirewallRules, patch.RouteFirewallRules...)
nm.ForwardingRules = append(nm.ForwardingRules, patch.ForwardingRules...)
if len(nm.RemotePeers) > 0 {
nm.RemotePeersIsEmpty = false
}
if len(nm.FirewallRules) > 0 {
nm.FirewallRulesIsEmpty = false
}
if len(nm.RoutesFirewallRules) > 0 {
nm.RoutesFirewallRulesIsEmpty = false
}
}
// appendUniquePeers dedupes by WgPubKey — mirrors legacy
// mergeUniquePeersByID's intent (legacy keyed off Peer.ID; in proto form the
// closest stable identifier is WgPubKey).
func appendUniquePeers(dst, extra []*proto.RemotePeerConfig) []*proto.RemotePeerConfig {
if len(extra) == 0 {
return dst
}
seen := make(map[string]struct{}, len(dst))
for _, p := range dst {
if p == nil {
continue
}
seen[p.WgPubKey] = struct{}{}
}
for _, p := range extra {
if p == nil {
continue
}
if _, ok := seen[p.WgPubKey]; ok {
continue
}
seen[p.WgPubKey] = struct{}{}
dst = append(dst, p)
}
return dst
}
func trimKey(s string) string {
if len(s) > 12 {
return s[:12]
File diff suppressed because it is too large Load Diff
+25 -24
View File
@@ -483,7 +483,8 @@ message NetworkMap {
// RoutesFirewallRulesIsEmpty indicates whether RouteFirewallRule array is empty or not to bypass protobuf null and empty array equality.
bool routesFirewallRulesIsEmpty = 11;
repeated ForwardingRule forwardingRules = 12;
// Unused; the ingress port-forwarding feature was discontinued.
repeated ForwardingRule forwardingRules = 12 [deprecated = true];
// SSHAuth represents SSH authorization configuration
SSHAuth sshAuth = 13;
@@ -566,8 +567,18 @@ message DeviceAuthorizationFlow {
}
}
// PKCEAuthorizationFlowRequest empty struct for future expansion
message PKCEAuthorizationFlowRequest {}
// PKCEAuthorizationFlowRequest asks for the PKCE flow configuration to use for
// an upcoming authorization request.
message PKCEAuthorizationFlowRequest {
// SessionExtend indicates the flow will renew the SSO session of a peer that
// is already registered, rather than log in or register one. An extend is
// bound to the account that peer belongs to, so the server must not answer it
// with a configuration that lets the IdP reply from whatever session is
// already active: with several accounts signed in at the IdP that need not be
// the peer's own, and the resulting token is rejected as a peer/user mismatch
// with no way for the user to correct it.
bool SessionExtend = 1;
}
// PKCEAuthorizationFlow represents Authorization Code Flow information
// that can be used by the client to login initiate a Oauth 2.0 authorization code grant flow
@@ -764,17 +775,13 @@ message RouteFirewallRule {
string RouteID = 10;
}
// ForwardingRule is unused; the ingress port-forwarding feature was discontinued.
message ForwardingRule {
// Protocol of the forwarding rule
option deprecated = true;
RuleProtocol protocol = 1;
// portInfo is the ingress destination port information, where the traffic arrives in the gateway node
PortInfo destinationPort = 2;
// IP address of the translated address (remote peer) to send traffic to
bytes translatedAddress = 3;
// Translated port information, where the traffic should be forwarded to
PortInfo translatedPort = 4;
}
@@ -820,7 +827,7 @@ message StopExposeResponse {}
// Component-based NetworkMap wire format (PeerCapabilityComponentNetworkMap).
//
// Peers that advertise this capability receive NetworkMap building blocks
// (peers + groups + policies + routes + dns + ssh + forwarding) and run the
// (peers + groups + policies + routes + dns + ssh) and run the
// expansion (Calculate) locally instead of receiving a fully-expanded
// NetworkMap from the server.
// =====================================================================
@@ -838,8 +845,8 @@ message NetworkMapEnvelope {
// client decodes it into a types.NetworkMapComponents and runs Calculate()
// locally to produce the same NetworkMap the legacy server path would have
// produced. Every field carries RAW component data — no server-side
// expansion (firewall rules, DNS config, SSH auth, route firewall rules,
// forwarding rules) is shipped; the client computes those itself.
// expansion (firewall rules, DNS config, SSH auth, route firewall rules)
// is shipped; the client computes those itself.
message NetworkMapComponentsFull {
uint64 serial = 1;
@@ -923,12 +930,8 @@ message NetworkMapComponentsFull {
// versions; clients fold it into their Calculate() DNS output.
int64 dns_forwarder_port = 23;
// Pre-expanded NetworkMap fragments injected post-Calculate by external
// controllers (BYOP / port-forwarding proxies). The receiving client
// merges these into its locally-computed NetworkMap the same way the
// legacy server does via NetworkMap.Merge — so downstream consumers see
// a unified merged result regardless of source.
ProxyPatch proxy_patch = 24;
// Unused; the ingress port-forwarding feature was discontinued.
ProxyPatch proxy_patch = 24 [deprecated = true];
// SSH UserIDClaim — server-side HttpServerConfig.AuthUserIDClaim, or
// "sub" by default. Populated in proto.SSHAuth.UserIDClaim when the
@@ -941,12 +944,10 @@ message NetworkMapComponentsFull {
reserved 26 to 50;
}
// ProxyPatch carries NetworkMap fragments that don't fit the component-graph
// model — they're pre-expanded by external controllers (BYOP /
// port-forwarding proxies) and injected post-Calculate. Fields use the
// legacy wire types because the proxy delivers them pre-formed; there is
// no raw component shape to convert from. Empty when no proxy is active.
// ProxyPatch is unused; the ingress port-forwarding feature was discontinued.
message ProxyPatch {
option deprecated = true;
repeated RemotePeerConfig peers = 1;
repeated RemotePeerConfig offline_peers = 2;
repeated FirewallRule firewall_rules = 3;
+232 -207
View File
@@ -2265,8 +2265,11 @@ type ValidateSessionRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
// Deprecated: Do not use.
SessionToken string `protobuf:"bytes,2,opt,name=session_token,json=sessionToken,proto3" json:"session_token,omitempty"`
// session_code is a short-lived, single-use code exchanged for a session token.
SessionCode string `protobuf:"bytes,3,opt,name=session_code,json=sessionCode,proto3" json:"session_code,omitempty"`
}
func (x *ValidateSessionRequest) Reset() {
@@ -2308,6 +2311,7 @@ func (x *ValidateSessionRequest) GetDomain() string {
return ""
}
// Deprecated: Do not use.
func (x *ValidateSessionRequest) GetSessionToken() string {
if x != nil {
return x.SessionToken
@@ -2315,6 +2319,13 @@ func (x *ValidateSessionRequest) GetSessionToken() string {
return ""
}
func (x *ValidateSessionRequest) GetSessionCode() string {
if x != nil {
return x.SessionCode
}
return ""
}
type ValidateSessionResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -2333,6 +2344,8 @@ type ValidateSessionResponse struct {
// Stamped onto upstream requests as X-NetBird-Groups so downstream
// services can read names rather than opaque ids.
PeerGroupNames []string `protobuf:"bytes,6,rep,name=peer_group_names,json=peerGroupNames,proto3" json:"peer_group_names,omitempty"`
// session_token contains the durable token issued when session_code is redeemed.
SessionToken string `protobuf:"bytes,7,opt,name=session_token,json=sessionToken,proto3" json:"session_token,omitempty"`
}
func (x *ValidateSessionResponse) Reset() {
@@ -2409,6 +2422,13 @@ func (x *ValidateSessionResponse) GetPeerGroupNames() []string {
return nil
}
func (x *ValidateSessionResponse) GetSessionToken() string {
if x != nil {
return x.SessionToken
}
return ""
}
// ValidateTunnelPeerRequest carries the inbound peer's tunnel IP and the
// service domain whose group requirements should gate access. The calling
// account is inferred from the proxy's gRPC metadata (ProxyToken).
@@ -3519,223 +3539,228 @@ var file_proxy_service_proto_rawDesc = []byte{
0x09, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x6c, 0x22, 0x26,
0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x55, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61,
0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x7c, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61,
0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xdc, 0x01,
0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x27, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42,
0x02, 0x18, 0x01, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65,
0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x43, 0x6f, 0x64, 0x65, 0x22, 0x81, 0x02, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12,
0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23,
0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x52, 0x65, 0x61,
0x73, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75,
0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65,
0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x65, 0x65,
0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20,
0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61,
0x6d, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x50, 0x0a, 0x19, 0x56, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f,
0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c,
0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x84, 0x02, 0x0a, 0x1a, 0x56,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65,
0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12,
0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72,
0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73,
0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65,
0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e,
0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05,
0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49,
0x64, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65,
0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x19,
0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65,
0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x75, 0x6e,
0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x75,
0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x84,
0x02, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65,
0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a,
0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x64,
0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70,
0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70,
0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18,
0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70,
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61,
0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a,
0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70,
0x70, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69,
0x74, 0x12, 0x2f, 0x0a, 0x03, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63,
0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x03, 0x61,
0x63, 0x6b, 0x42, 0x05, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xdf, 0x01, 0x0a, 0x10, 0x53, 0x79,
0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x19,
0x0a, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x18,
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x41, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61,
0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63,
0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53,
0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, 0x22, 0x7e,
0x0a, 0x14, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e,
0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
0x67, 0x52, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e,
0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c,
0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x69, 0x74, 0x69,
0x61, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x22, 0xa9,
0x01, 0x0a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63,
0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d,
0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a,
0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f,
0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70,
0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0xff, 0x01, 0x0a, 0x1c, 0x43,
0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d,
0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65,
0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f,
0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47,
0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x65, 0x65, 0x72, 0x5f,
0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28,
0x09, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65,
0x73, 0x22, 0x81, 0x01, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x69, 0x6e, 0x69,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
0x73, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2f, 0x0a,
0x03, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70,
0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x03, 0x61, 0x63, 0x6b, 0x42, 0x05,
0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xdf, 0x01, 0x0a, 0x10, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61,
0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12,
0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x12, 0x41, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69,
0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x61, 0x70,
0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62,
0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x79, 0x6e, 0x63, 0x4d,
0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, 0x22, 0x7e, 0x0a, 0x14, 0x53, 0x79,
0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x32, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x6d,
0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61,
0x6c, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x79,
0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x22, 0xa9, 0x01, 0x0a, 0x1b, 0x43,
0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d,
0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64,
0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64,
0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63,
0x74, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x6c,
0x69, 0x63, 0x79, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e,
0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x6f,
0x77, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1b,
0x0a, 0x09, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x64, 0x65, 0x6e, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64,
0x65, 0x6e, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x64, 0x65, 0x6e, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x91, 0x02, 0x0a,
0x15, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19,
0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e,
0x64, 0x6f, 0x77, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
0x03, 0x52, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73,
0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x49, 0x6e,
0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6f, 0x73, 0x74,
0x5f, 0x75, 0x73, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x63, 0x6f, 0x73, 0x74,
0x55, 0x73, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73,
0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73,
0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61,
0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x64, 0x0a, 0x16, 0x50, 0x72,
0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54,
0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a,
0x14, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x4f, 0x44,
0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, 0x41, 0x54,
0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x02,
0x2a, 0x46, 0x0a, 0x0f, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x4d,
0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x41, 0x54, 0x48, 0x5f, 0x52, 0x45, 0x57, 0x52,
0x49, 0x54, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x19, 0x0a,
0x15, 0x50, 0x41, 0x54, 0x48, 0x5f, 0x52, 0x45, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x50, 0x52,
0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x10, 0x01, 0x2a, 0x90, 0x01, 0x0a, 0x0e, 0x4d, 0x69, 0x64,
0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x4d,
0x49, 0x44, 0x44, 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x55,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a,
0x4d, 0x49, 0x44, 0x44, 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f,
0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b,
0x4d, 0x49, 0x44, 0x44, 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f,
0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a,
0x18, 0x4d, 0x49, 0x44, 0x44, 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54,
0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0xc8, 0x01, 0x0a, 0x0b,
0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x14, 0x50,
0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44,
0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53,
0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x23,
0x0a, 0x1f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54,
0x55, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
0x44, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41,
0x54, 0x55, 0x53, 0x5f, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f,
0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, 0x4f,
0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46,
0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x16,
0x0a, 0x12, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45,
0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x32, 0xfc, 0x07, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61,
0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70,
0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x24, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65,
0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x55, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63,
0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69,
0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12,
0x54, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67,
0x12, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65,
0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53,
0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x6d, 0x61, 0x6e,
0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65,
0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72,
0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12,
0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64,
0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0xff, 0x01, 0x0a, 0x1c, 0x43, 0x68, 0x65, 0x63, 0x6b,
0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f,
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49,
0x64, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75,
0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x65,
0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x77, 0x69, 0x6e,
0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65,
0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64,
0x65, 0x6e, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x6e, 0x79, 0x5f,
0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65,
0x6e, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x91, 0x02, 0x0a, 0x15, 0x52, 0x65, 0x63,
0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49,
0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f,
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x77,
0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01,
0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12,
0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x4f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x64,
0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x63, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x64, 0x12,
0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03,
0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x18, 0x0a, 0x16,
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x64, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d,
0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65,
0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x50, 0x44,
0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45,
0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x46, 0x0a, 0x0f,
0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12,
0x18, 0x0a, 0x14, 0x50, 0x41, 0x54, 0x48, 0x5f, 0x52, 0x45, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f,
0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x50, 0x41, 0x54,
0x48, 0x5f, 0x52, 0x45, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x53, 0x45, 0x52,
0x56, 0x45, 0x10, 0x01, 0x2a, 0x90, 0x01, 0x0a, 0x0e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
0x61, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x49, 0x44, 0x44, 0x4c,
0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x49, 0x44, 0x44,
0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x52,
0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x49, 0x44, 0x44,
0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x52,
0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x49, 0x44,
0x44, 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x54, 0x45, 0x52,
0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0xc8, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x52, 0x4f, 0x58, 0x59,
0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10,
0x00, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55,
0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52,
0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x55, 0x4e, 0x4e, 0x45,
0x4c, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12,
0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f,
0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44,
0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53,
0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54,
0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52,
0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52,
0x10, 0x05, 0x32, 0xfc, 0x07, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70,
0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x30, 0x01, 0x12, 0x55, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70,
0x69, 0x6e, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x53,
0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x12, 0x20, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63,
0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64,
0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x51, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74,
0x65, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41,
0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f,
0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50,
0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72,
0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52,
0x4c, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47,
0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65,
0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x5a, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x4b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x12, 0x1d, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x49,
0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44,
0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f,
0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12,
0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12,
0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65,
0x65, 0x72, 0x12, 0x25, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65,
0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54,
0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x69, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c,
0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69,
0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e,
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f,
0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52,
0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x56, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x12, 0x25,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65,
0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a,
0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c,
0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63,
0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63,
0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x6f,
0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c,
0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72,
0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
+5 -2
View File
@@ -363,7 +363,9 @@ message GetOIDCURLResponse {
message ValidateSessionRequest {
string domain = 1;
string session_token = 2;
string session_token = 2 [deprecated = true];
// session_code is a short-lived, single-use code exchanged for a session token.
string session_code = 3;
}
message ValidateSessionResponse {
@@ -380,6 +382,8 @@ message ValidateSessionResponse {
// Stamped onto upstream requests as X-NetBird-Groups so downstream
// services can read names rather than opaque ids.
repeated string peer_group_names = 6;
// session_token contains the durable token issued when session_code is redeemed.
string session_token = 7;
}
// ValidateTunnelPeerRequest carries the inbound peer's tunnel IP and the
@@ -506,4 +510,3 @@ message RecordLLMUsageRequest {
message RecordLLMUsageResponse {
}
-104
View File
@@ -1,13 +1,8 @@
package types
import (
"net"
"golang.org/x/exp/maps"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
"github.com/netbirdio/netbird/shared/management/proto"
)
const (
@@ -25,7 +20,6 @@ type NetworkMap struct {
OfflinePeers []*nmdata.Peer
FirewallRules []*FirewallRule
RoutesFirewallRules []*RouteFirewallRule
ForwardingRules []*ForwardingRule
AuthorizedUsers map[string]map[string]struct{}
EnableSSH bool
// ForceRoutingPeerDNSResolution forces the peer to run/use routing-peer DNS
@@ -33,101 +27,3 @@ type NetworkMap struct {
// domain targets.
ForceRoutingPeerDNSResolution bool
}
func (nm *NetworkMap) Merge(other *NetworkMap) {
nm.Peers = mergeUniquePeersByID(nm.Peers, other.Peers)
nm.Routes = mergeUnique(nm.Routes, other.Routes)
nm.OfflinePeers = mergeUniquePeersByID(nm.OfflinePeers, other.OfflinePeers)
nm.FirewallRules = mergeUnique(nm.FirewallRules, other.FirewallRules)
nm.RoutesFirewallRules = mergeUnique(nm.RoutesFirewallRules, other.RoutesFirewallRules)
nm.ForwardingRules = mergeUnique(nm.ForwardingRules, other.ForwardingRules)
nm.ForceRoutingPeerDNSResolution = nm.ForceRoutingPeerDNSResolution || other.ForceRoutingPeerDNSResolution
}
func mergeUniquePeersByID(peers1, peers2 []*nmdata.Peer) []*nmdata.Peer {
result := make(map[string]*nmdata.Peer)
for _, peer := range peers1 {
result[peer.ID] = peer
}
for _, peer := range peers2 {
if _, ok := result[peer.ID]; !ok {
result[peer.ID] = peer
}
}
return maps.Values(result)
}
type ForwardingRule struct {
RuleProtocol string
DestinationPorts RulePortRange
TranslatedAddress net.IP
TranslatedPorts RulePortRange
}
func (f *ForwardingRule) ToProto() *proto.ForwardingRule {
var protocol proto.RuleProtocol
switch f.RuleProtocol {
case "icmp":
protocol = proto.RuleProtocol_ICMP
case "tcp":
protocol = proto.RuleProtocol_TCP
case "udp":
protocol = proto.RuleProtocol_UDP
case "all":
protocol = proto.RuleProtocol_ALL
default:
protocol = proto.RuleProtocol_UNKNOWN
}
return &proto.ForwardingRule{
Protocol: protocol,
DestinationPort: f.DestinationPorts.ToProto(),
TranslatedAddress: ipToBytes(f.TranslatedAddress),
TranslatedPort: f.TranslatedPorts.ToProto(),
}
}
func (f *ForwardingRule) Equal(other *ForwardingRule) bool {
return f.RuleProtocol == other.RuleProtocol &&
f.DestinationPorts.Equal(&other.DestinationPorts) &&
f.TranslatedAddress.Equal(other.TranslatedAddress) &&
f.TranslatedPorts.Equal(&other.TranslatedPorts)
}
func ipToBytes(ip net.IP) []byte {
if ip4 := ip.To4(); ip4 != nil {
return ip4
}
return ip.To16()
}
type comparableObject[T any] interface {
Equal(other T) bool
}
func mergeUnique[T comparableObject[T]](arr1, arr2 []T) []T {
var result []T
for _, item := range arr1 {
if !containsEqual(result, item) {
result = append(result, item)
}
}
for _, item := range arr2 {
if !containsEqual(result, item) {
result = append(result, item)
}
}
return result
}
func containsEqual[T comparableObject[T]](slice []T, element T) bool {
for _, item := range slice {
if item.Equal(element) {
return true
}
}
return false
}
-41
View File
@@ -1,41 +0,0 @@
package types
import (
"testing"
"github.com/stretchr/testify/assert"
)
type mergeTestObject struct {
value int
}
func (t mergeTestObject) Equal(other mergeTestObject) bool {
return t.value == other.value
}
func Test_MergeUniqueArraysWithoutDuplicates(t *testing.T) {
arr1 := []mergeTestObject{{value: 1}, {value: 2}}
arr2 := []mergeTestObject{{value: 2}, {value: 3}}
result := mergeUnique(arr1, arr2)
assert.Len(t, result, 3)
assert.Contains(t, result, mergeTestObject{value: 1})
assert.Contains(t, result, mergeTestObject{value: 2})
assert.Contains(t, result, mergeTestObject{value: 3})
}
func Test_MergeUniqueHandlesEmptyArrays(t *testing.T) {
arr1 := []mergeTestObject{}
arr2 := []mergeTestObject{}
result := mergeUnique(arr1, arr2)
assert.Empty(t, result)
}
func Test_MergeUniqueHandlesOneEmptyArray(t *testing.T) {
arr1 := []mergeTestObject{{value: 1}, {value: 2}}
arr2 := []mergeTestObject{}
result := mergeUnique(arr1, arr2)
assert.Len(t, result, 2)
assert.Contains(t, result, mergeTestObject{value: 1})
assert.Contains(t, result, mergeTestObject{value: 2})
}