Merge origin/main into feature/fleetdm

Resolve OpenAPI spec conflict by keeping both FleetDM schemas
and new IntegrationSyncFilters/IntegrationEnabled schemas.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mlsmaycon
2026-03-31 12:26:24 +02:00
co-authored by Claude Opus 4.6
183 changed files with 57744 additions and 2162 deletions
+1
View File
@@ -22,6 +22,7 @@ type Client interface {
GetDeviceAuthorizationFlow(serverKey wgtypes.Key) (*proto.DeviceAuthorizationFlow, error)
GetPKCEAuthorizationFlow(serverKey wgtypes.Key) (*proto.PKCEAuthorizationFlow, error)
GetNetworkMap(sysInfo *system.Info) (*proto.NetworkMap, error)
GetServerURL() string
IsHealthy() bool
SyncMeta(sysInfo *system.Info) error
Logout() error
+42 -1
View File
@@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"io"
"os"
"strconv"
"sync"
"time"
@@ -29,6 +31,10 @@ import (
const ConnectTimeout = 10 * time.Second
const (
// EnvMaxRecvMsgSize overrides the default gRPC max receive message size (4 MB)
// for the management client connection. Value is in bytes.
EnvMaxRecvMsgSize = "NB_MANAGEMENT_GRPC_MAX_MSG_SIZE"
errMsgMgmtPublicKey = "failed getting Management Service public key: %s"
errMsgNoMgmtConnection = "no connection to management"
)
@@ -46,6 +52,7 @@ type GrpcClient struct {
conn *grpc.ClientConn
connStateCallback ConnStateNotifier
connStateCallbackLock sync.RWMutex
serverURL string
}
type ExposeRequest struct {
@@ -66,13 +73,41 @@ type ExposeResponse struct {
PortAutoAssigned bool
}
// MaxRecvMsgSize returns the configured max gRPC receive message size from
// the environment, or 0 if unset (which uses the gRPC default of 4 MB).
func MaxRecvMsgSize() int {
val := os.Getenv(EnvMaxRecvMsgSize)
if val == "" {
return 0
}
size, err := strconv.Atoi(val)
if err != nil {
log.Warnf("invalid %s value %q, using default: %v", EnvMaxRecvMsgSize, val, err)
return 0
}
if size <= 0 {
log.Warnf("invalid %s value %d, must be positive, using default", EnvMaxRecvMsgSize, size)
return 0
}
return size
}
// NewClient creates a new client to Management service
func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsEnabled bool) (*GrpcClient, error) {
var conn *grpc.ClientConn
var extraOpts []grpc.DialOption
if maxSize := MaxRecvMsgSize(); maxSize > 0 {
extraOpts = append(extraOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxSize)))
log.Infof("management gRPC max receive message size set to %d bytes", maxSize)
}
operation := func() error {
var err error
conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.ManagementComponent)
conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.ManagementComponent, extraOpts...)
if err != nil {
return fmt.Errorf("create connection: %w", err)
}
@@ -93,9 +128,15 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
ctx: ctx,
conn: conn,
connStateCallbackLock: sync.RWMutex{},
serverURL: addr,
}, nil
}
// GetServerURL returns the management server URL
func (c *GrpcClient) GetServerURL() string {
return c.serverURL
}
// Close closes connection to the Management Service
func (c *GrpcClient) Close() error {
return c.conn.Close()
+95
View File
@@ -0,0 +1,95 @@
package client
import (
"context"
"net"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
)
func TestMaxRecvMsgSize(t *testing.T) {
tests := []struct {
name string
envValue string
expected int
}{
{name: "unset returns 0", envValue: "", expected: 0},
{name: "valid value", envValue: "10485760", expected: 10485760},
{name: "non-numeric returns 0", envValue: "abc", expected: 0},
{name: "negative returns 0", envValue: "-1", expected: 0},
{name: "zero returns 0", envValue: "0", expected: 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Setenv(EnvMaxRecvMsgSize, tt.envValue)
if tt.envValue == "" {
os.Unsetenv(EnvMaxRecvMsgSize)
}
assert.Equal(t, tt.expected, MaxRecvMsgSize())
})
}
}
// largeSyncServer implements just the Sync RPC, returning a response larger than the default 4MB limit.
type largeSyncServer struct {
mgmtProto.UnimplementedManagementServiceServer
responseSize int
}
func (s *largeSyncServer) GetServerKey(_ context.Context, _ *mgmtProto.Empty) (*mgmtProto.ServerKeyResponse, error) {
// Return a response with a large WiretrusteeConfig to exceed the default limit.
padding := strings.Repeat("x", s.responseSize)
return &mgmtProto.ServerKeyResponse{
Key: padding,
}, nil
}
func TestMaxRecvMsgSizeIntegration(t *testing.T) {
const payloadSize = 5 * 1024 * 1024 // 5MB, exceeds 4MB default
lis, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
srv := grpc.NewServer()
mgmtProto.RegisterManagementServiceServer(srv, &largeSyncServer{responseSize: payloadSize})
go func() { _ = srv.Serve(lis) }()
t.Cleanup(srv.Stop)
t.Run("default limit rejects large message", func(t *testing.T) {
conn, err := grpc.NewClient(
lis.Addr().String(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
require.NoError(t, err)
defer conn.Close()
client := mgmtProto.NewManagementServiceClient(conn)
_, err = client.GetServerKey(context.Background(), &mgmtProto.Empty{})
require.Error(t, err)
assert.Contains(t, err.Error(), "received message larger than max")
})
t.Run("increased limit accepts large message", func(t *testing.T) {
conn, err := grpc.NewClient(
lis.Addr().String(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(10*1024*1024)),
)
require.NoError(t, err)
defer conn.Close()
client := mgmtProto.NewManagementServiceClient(conn)
resp, err := client.GetServerKey(context.Background(), &mgmtProto.Empty{})
require.NoError(t, err)
assert.Len(t, resp.Key, payloadSize)
})
}
+9
View File
@@ -19,6 +19,7 @@ type MockClient struct {
LoginFunc func(serverKey wgtypes.Key, info *system.Info, sshKey []byte, dnsLabels domain.List) (*proto.LoginResponse, error)
GetDeviceAuthorizationFlowFunc func(serverKey wgtypes.Key) (*proto.DeviceAuthorizationFlow, error)
GetPKCEAuthorizationFlowFunc func(serverKey wgtypes.Key) (*proto.PKCEAuthorizationFlow, error)
GetServerURLFunc func() string
SyncMetaFunc func(sysInfo *system.Info) error
LogoutFunc func() error
JobFunc func(ctx context.Context, msgHandler func(msg *proto.JobRequest) *proto.JobResponse) error
@@ -92,6 +93,14 @@ func (m *MockClient) GetNetworkMap(_ *system.Info) (*proto.NetworkMap, error) {
return nil, nil
}
// GetServerURL mock implementation of GetServerURL from mgm.Client interface
func (m *MockClient) GetServerURL() string {
if m.GetServerURLFunc == nil {
return ""
}
return m.GetServerURLFunc()
}
func (m *MockClient) SyncMeta(sysInfo *system.Info) error {
if m.SyncMetaFunc == nil {
return nil
+112
View File
@@ -0,0 +1,112 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// AzureIDPAPI APIs for Azure AD IDP integrations
type AzureIDPAPI struct {
c *Client
}
// List retrieves all Azure AD IDP integrations
func (a *AzureIDPAPI) List(ctx context.Context) ([]api.AzureIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/azure-idp", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.AzureIntegration](resp)
return ret, err
}
// Get retrieves a specific Azure AD IDP integration by ID
func (a *AzureIDPAPI) Get(ctx context.Context, integrationID string) (*api.AzureIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/azure-idp/"+integrationID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.AzureIntegration](resp)
return &ret, err
}
// Create creates a new Azure AD IDP integration
func (a *AzureIDPAPI) Create(ctx context.Context, request api.CreateAzureIntegrationRequest) (*api.AzureIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/azure-idp", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.AzureIntegration](resp)
return &ret, err
}
// Update updates an existing Azure AD IDP integration
func (a *AzureIDPAPI) Update(ctx context.Context, integrationID string, request api.UpdateAzureIntegrationRequest) (*api.AzureIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/azure-idp/"+integrationID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.AzureIntegration](resp)
return &ret, err
}
// Delete deletes an Azure AD IDP integration
func (a *AzureIDPAPI) Delete(ctx context.Context, integrationID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/azure-idp/"+integrationID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// Sync triggers a manual sync for an Azure AD IDP integration
func (a *AzureIDPAPI) Sync(ctx context.Context, integrationID string) (*api.SyncResult, error) {
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/azure-idp/"+integrationID+"/sync", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.SyncResult](resp)
return &ret, err
}
// GetLogs retrieves synchronization logs for an Azure AD IDP integration
func (a *AzureIDPAPI) GetLogs(ctx context.Context, integrationID string) ([]api.IdpIntegrationSyncLog, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/azure-idp/"+integrationID+"/logs", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IdpIntegrationSyncLog](resp)
return ret, err
}
@@ -0,0 +1,252 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"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 testAzureIntegration = api.AzureIntegration{
Id: 1,
Enabled: true,
ClientId: "12345678-1234-1234-1234-123456789012",
TenantId: "87654321-4321-4321-4321-210987654321",
SyncInterval: 300,
GroupPrefixes: []string{"eng-"},
UserGroupPrefixes: []string{"dev-"},
Host: "microsoft.com",
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
func TestAzureIDP_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.AzureIntegration{testAzureIntegration})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.AzureIDP.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testAzureIntegration, ret[0])
})
}
func TestAzureIDP_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp", 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.AzureIDP.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestAzureIDP_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testAzureIntegration)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.AzureIDP.Get(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, testAzureIntegration, *ret)
})
}
func TestAzureIDP_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-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)
})
ret, err := c.AzureIDP.Get(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestAzureIDP_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp", 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.CreateAzureIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "12345678-1234-1234-1234-123456789012", req.ClientId)
retBytes, _ := json.Marshal(testAzureIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.AzureIDP.Create(context.Background(), api.CreateAzureIntegrationRequest{
ClientId: "12345678-1234-1234-1234-123456789012",
ClientSecret: "secret",
TenantId: "87654321-4321-4321-4321-210987654321",
Host: api.CreateAzureIntegrationRequestHostMicrosoftCom,
GroupPrefixes: &[]string{"eng-"},
})
require.NoError(t, err)
assert.Equal(t, testAzureIntegration, *ret)
})
}
func TestAzureIDP_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp", 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.AzureIDP.Create(context.Background(), api.CreateAzureIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestAzureIDP_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-1", 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.UpdateAzureIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, *req.Enabled)
retBytes, _ := json.Marshal(testAzureIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.AzureIDP.Update(context.Background(), "int-1", api.UpdateAzureIntegrationRequest{
Enabled: ptr(true),
})
require.NoError(t, err)
assert.Equal(t, testAzureIntegration, *ret)
})
}
func TestAzureIDP_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-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.AzureIDP.Update(context.Background(), "int-1", api.UpdateAzureIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestAzureIDP_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.AzureIDP.Delete(context.Background(), "int-1")
require.NoError(t, err)
})
}
func TestAzureIDP_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-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.AzureIDP.Delete(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestAzureIDP_Sync_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-1/sync", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(api.SyncResult{Result: ptr("ok")})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.AzureIDP.Sync(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, "ok", *ret.Result)
})
}
func TestAzureIDP_Sync_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-1/sync", 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)
})
ret, err := c.AzureIDP.Sync(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestAzureIDP_GetLogs_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-1/logs", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.IdpIntegrationSyncLog{testSyncLog})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.AzureIDP.GetLogs(context.Background(), "int-1")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testSyncLog, ret[0])
})
}
func TestAzureIDP_GetLogs_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/azure-idp/int-1/logs", 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)
})
ret, err := c.AzureIDP.GetLogs(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Empty(t, ret)
})
}
+12
View File
@@ -110,6 +110,15 @@ type Client struct {
// see more: https://docs.netbird.io/api/resources/scim
SCIM *SCIMAPI
// GoogleIDP NetBird Google Workspace IDP integration APIs
GoogleIDP *GoogleIDPAPI
// AzureIDP NetBird Azure AD IDP integration APIs
AzureIDP *AzureIDPAPI
// OktaScimIDP NetBird Okta SCIM IDP integration APIs
OktaScimIDP *OktaScimIDPAPI
// EventStreaming NetBird Event Streaming integration APIs
// see more: https://docs.netbird.io/api/resources/event-streaming
EventStreaming *EventStreamingAPI
@@ -185,6 +194,9 @@ func (c *Client) initialize() {
c.MSP = &MSPAPI{c}
c.EDR = &EDRAPI{c}
c.SCIM = &SCIMAPI{c}
c.GoogleIDP = &GoogleIDPAPI{c}
c.AzureIDP = &AzureIDPAPI{c}
c.OktaScimIDP = &OktaScimIDPAPI{c}
c.EventStreaming = &EventStreamingAPI{c}
c.IdentityProviders = &IdentityProvidersAPI{c}
c.Ingress = &IngressAPI{c}
+112
View File
@@ -0,0 +1,112 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// GoogleIDPAPI APIs for Google Workspace IDP integrations
type GoogleIDPAPI struct {
c *Client
}
// List retrieves all Google Workspace IDP integrations
func (a *GoogleIDPAPI) List(ctx context.Context) ([]api.GoogleIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/google-idp", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.GoogleIntegration](resp)
return ret, err
}
// Get retrieves a specific Google Workspace IDP integration by ID
func (a *GoogleIDPAPI) Get(ctx context.Context, integrationID string) (*api.GoogleIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/google-idp/"+integrationID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.GoogleIntegration](resp)
return &ret, err
}
// Create creates a new Google Workspace IDP integration
func (a *GoogleIDPAPI) Create(ctx context.Context, request api.CreateGoogleIntegrationRequest) (*api.GoogleIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/google-idp", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.GoogleIntegration](resp)
return &ret, err
}
// Update updates an existing Google Workspace IDP integration
func (a *GoogleIDPAPI) Update(ctx context.Context, integrationID string, request api.UpdateGoogleIntegrationRequest) (*api.GoogleIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/google-idp/"+integrationID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.GoogleIntegration](resp)
return &ret, err
}
// Delete deletes a Google Workspace IDP integration
func (a *GoogleIDPAPI) Delete(ctx context.Context, integrationID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/google-idp/"+integrationID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// Sync triggers a manual sync for a Google Workspace IDP integration
func (a *GoogleIDPAPI) Sync(ctx context.Context, integrationID string) (*api.SyncResult, error) {
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/google-idp/"+integrationID+"/sync", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.SyncResult](resp)
return &ret, err
}
// GetLogs retrieves synchronization logs for a Google Workspace IDP integration
func (a *GoogleIDPAPI) GetLogs(ctx context.Context, integrationID string) ([]api.IdpIntegrationSyncLog, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/google-idp/"+integrationID+"/logs", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IdpIntegrationSyncLog](resp)
return ret, err
}
@@ -0,0 +1,248 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"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 testGoogleIntegration = api.GoogleIntegration{
Id: 1,
Enabled: true,
CustomerId: "C01234567",
SyncInterval: 300,
GroupPrefixes: []string{"eng-"},
UserGroupPrefixes: []string{"dev-"},
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
func TestGoogleIDP_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.GoogleIntegration{testGoogleIntegration})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GoogleIDP.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testGoogleIntegration, ret[0])
})
}
func TestGoogleIDP_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp", 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.GoogleIDP.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestGoogleIDP_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testGoogleIntegration)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GoogleIDP.Get(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, testGoogleIntegration, *ret)
})
}
func TestGoogleIDP_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-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)
})
ret, err := c.GoogleIDP.Get(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestGoogleIDP_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp", 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.CreateGoogleIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "C01234567", req.CustomerId)
retBytes, _ := json.Marshal(testGoogleIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GoogleIDP.Create(context.Background(), api.CreateGoogleIntegrationRequest{
CustomerId: "C01234567",
ServiceAccountKey: "key-data",
GroupPrefixes: &[]string{"eng-"},
})
require.NoError(t, err)
assert.Equal(t, testGoogleIntegration, *ret)
})
}
func TestGoogleIDP_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp", 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.GoogleIDP.Create(context.Background(), api.CreateGoogleIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestGoogleIDP_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-1", 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.UpdateGoogleIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, *req.Enabled)
retBytes, _ := json.Marshal(testGoogleIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GoogleIDP.Update(context.Background(), "int-1", api.UpdateGoogleIntegrationRequest{
Enabled: ptr(true),
})
require.NoError(t, err)
assert.Equal(t, testGoogleIntegration, *ret)
})
}
func TestGoogleIDP_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-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.GoogleIDP.Update(context.Background(), "int-1", api.UpdateGoogleIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestGoogleIDP_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.GoogleIDP.Delete(context.Background(), "int-1")
require.NoError(t, err)
})
}
func TestGoogleIDP_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-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.GoogleIDP.Delete(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestGoogleIDP_Sync_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-1/sync", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(api.SyncResult{Result: ptr("ok")})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GoogleIDP.Sync(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, "ok", *ret.Result)
})
}
func TestGoogleIDP_Sync_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-1/sync", 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)
})
ret, err := c.GoogleIDP.Sync(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestGoogleIDP_GetLogs_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-1/logs", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.IdpIntegrationSyncLog{testSyncLog})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.GoogleIDP.GetLogs(context.Background(), "int-1")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testSyncLog, ret[0])
})
}
func TestGoogleIDP_GetLogs_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/google-idp/int-1/logs", 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)
})
ret, err := c.GoogleIDP.GetLogs(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Empty(t, ret)
})
}
@@ -0,0 +1,112 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// OktaScimIDPAPI APIs for Okta SCIM IDP integrations
type OktaScimIDPAPI struct {
c *Client
}
// List retrieves all Okta SCIM IDP integrations
func (a *OktaScimIDPAPI) List(ctx context.Context) ([]api.OktaScimIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/okta-scim-idp", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.OktaScimIntegration](resp)
return ret, err
}
// Get retrieves a specific Okta SCIM IDP integration by ID
func (a *OktaScimIDPAPI) Get(ctx context.Context, integrationID string) (*api.OktaScimIntegration, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/okta-scim-idp/"+integrationID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.OktaScimIntegration](resp)
return &ret, err
}
// Create creates a new Okta SCIM IDP integration
func (a *OktaScimIDPAPI) Create(ctx context.Context, request api.CreateOktaScimIntegrationRequest) (*api.OktaScimIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/okta-scim-idp", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.OktaScimIntegration](resp)
return &ret, err
}
// Update updates an existing Okta SCIM IDP integration
func (a *OktaScimIDPAPI) Update(ctx context.Context, integrationID string, request api.UpdateOktaScimIntegrationRequest) (*api.OktaScimIntegration, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/integrations/okta-scim-idp/"+integrationID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.OktaScimIntegration](resp)
return &ret, err
}
// Delete deletes an Okta SCIM IDP integration
func (a *OktaScimIDPAPI) Delete(ctx context.Context, integrationID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/integrations/okta-scim-idp/"+integrationID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// RegenerateToken regenerates the SCIM API token for an Okta SCIM integration
func (a *OktaScimIDPAPI) RegenerateToken(ctx context.Context, integrationID string) (*api.ScimTokenResponse, error) {
resp, err := a.c.NewRequest(ctx, "POST", "/api/integrations/okta-scim-idp/"+integrationID+"/token", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.ScimTokenResponse](resp)
return &ret, err
}
// GetLogs retrieves synchronization logs for an Okta SCIM IDP integration
func (a *OktaScimIDPAPI) GetLogs(ctx context.Context, integrationID string) ([]api.IdpIntegrationSyncLog, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/integrations/okta-scim-idp/"+integrationID+"/logs", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.IdpIntegrationSyncLog](resp)
return ret, err
}
@@ -0,0 +1,246 @@
//go:build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"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 testOktaScimIntegration = api.OktaScimIntegration{
Id: 1,
AuthToken: "****",
Enabled: true,
GroupPrefixes: []string{"eng-"},
UserGroupPrefixes: []string{"dev-"},
LastSyncedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
func TestOktaScimIDP_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.OktaScimIntegration{testOktaScimIntegration})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.OktaScimIDP.List(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testOktaScimIntegration, ret[0])
})
}
func TestOktaScimIDP_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp", 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.OktaScimIDP.List(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestOktaScimIDP_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testOktaScimIntegration)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.OktaScimIDP.Get(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, testOktaScimIntegration, *ret)
})
}
func TestOktaScimIDP_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-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)
})
ret, err := c.OktaScimIDP.Get(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestOktaScimIDP_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp", 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.CreateOktaScimIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "my-okta-connection", req.ConnectionName)
retBytes, _ := json.Marshal(testOktaScimIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.OktaScimIDP.Create(context.Background(), api.CreateOktaScimIntegrationRequest{
ConnectionName: "my-okta-connection",
GroupPrefixes: &[]string{"eng-"},
})
require.NoError(t, err)
assert.Equal(t, testOktaScimIntegration, *ret)
})
}
func TestOktaScimIDP_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp", 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.OktaScimIDP.Create(context.Background(), api.CreateOktaScimIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestOktaScimIDP_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-1", 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.UpdateOktaScimIntegrationRequest
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, true, *req.Enabled)
retBytes, _ := json.Marshal(testOktaScimIntegration)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.OktaScimIDP.Update(context.Background(), "int-1", api.UpdateOktaScimIntegrationRequest{
Enabled: ptr(true),
})
require.NoError(t, err)
assert.Equal(t, testOktaScimIntegration, *ret)
})
}
func TestOktaScimIDP_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-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.OktaScimIDP.Update(context.Background(), "int-1", api.UpdateOktaScimIntegrationRequest{})
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Nil(t, ret)
})
}
func TestOktaScimIDP_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-1", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.OktaScimIDP.Delete(context.Background(), "int-1")
require.NoError(t, err)
})
}
func TestOktaScimIDP_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-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.OktaScimIDP.Delete(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestOktaScimIDP_RegenerateToken_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-1/token", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
retBytes, _ := json.Marshal(testScimToken)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.OktaScimIDP.RegenerateToken(context.Background(), "int-1")
require.NoError(t, err)
assert.Equal(t, testScimToken, *ret)
})
}
func TestOktaScimIDP_RegenerateToken_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-1/token", 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)
})
ret, err := c.OktaScimIDP.RegenerateToken(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Nil(t, ret)
})
}
func TestOktaScimIDP_GetLogs_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-1/logs", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.IdpIntegrationSyncLog{testSyncLog})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.OktaScimIDP.GetLogs(context.Background(), "int-1")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testSyncLog, ret[0])
})
}
func TestOktaScimIDP_GetLogs_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/integrations/okta-scim-idp/int-1/logs", 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)
})
ret, err := c.OktaScimIDP.GetLogs(context.Background(), "int-1")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Empty(t, ret)
})
}
File diff suppressed because it is too large Load Diff
+527 -9
View File
@@ -9,6 +9,7 @@ import (
"time"
"github.com/oapi-codegen/runtime"
openapi_types "github.com/oapi-codegen/runtime/types"
)
const (
@@ -16,6 +17,24 @@ const (
TokenAuthScopes = "TokenAuth.Scopes"
)
// Defines values for CreateAzureIntegrationRequestHost.
const (
CreateAzureIntegrationRequestHostMicrosoftCom CreateAzureIntegrationRequestHost = "microsoft.com"
CreateAzureIntegrationRequestHostMicrosoftUs CreateAzureIntegrationRequestHost = "microsoft.us"
)
// Valid indicates whether the value is a known member of the CreateAzureIntegrationRequestHost enum.
func (e CreateAzureIntegrationRequestHost) Valid() bool {
switch e {
case CreateAzureIntegrationRequestHostMicrosoftCom:
return true
case CreateAzureIntegrationRequestHostMicrosoftUs:
return true
default:
return false
}
}
// Defines values for CreateIntegrationRequestPlatform.
const (
CreateIntegrationRequestPlatformDatadog CreateIntegrationRequestPlatform = "datadog"
@@ -664,6 +683,24 @@ func (e NetworkResourceType) Valid() bool {
}
}
// Defines values for NotificationChannelType.
const (
NotificationChannelTypeEmail NotificationChannelType = "email"
NotificationChannelTypeWebhook NotificationChannelType = "webhook"
)
// Valid indicates whether the value is a known member of the NotificationChannelType enum.
func (e NotificationChannelType) Valid() bool {
switch e {
case NotificationChannelTypeEmail:
return true
case NotificationChannelTypeWebhook:
return true
default:
return false
}
}
// Defines values for PeerNetworkRangeCheckAction.
const (
PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow"
@@ -1276,6 +1313,21 @@ func (e PutApiIntegrationsMspTenantsIdInviteJSONBodyValue) Valid() bool {
}
}
// AccessRestrictions Connection-level access restrictions based on IP address or geography. Applies to both HTTP and L4 services.
type AccessRestrictions struct {
// AllowedCidrs CIDR allowlist. If non-empty, only IPs matching these CIDRs are allowed.
AllowedCidrs *[]string `json:"allowed_cidrs,omitempty"`
// AllowedCountries ISO 3166-1 alpha-2 country codes to allow. If non-empty, only these countries are permitted.
AllowedCountries *[]string `json:"allowed_countries,omitempty"`
// BlockedCidrs CIDR blocklist. Connections from these CIDRs are rejected. Evaluated after allowed_cidrs.
BlockedCidrs *[]string `json:"blocked_cidrs,omitempty"`
// BlockedCountries ISO 3166-1 alpha-2 country codes to block.
BlockedCountries *[]string `json:"blocked_countries,omitempty"`
}
// AccessiblePeer defines model for AccessiblePeer.
type AccessiblePeer struct {
// CityName Commonly used English name of the city
@@ -1435,6 +1487,36 @@ type AvailablePorts struct {
Udp int `json:"udp"`
}
// AzureIntegration defines model for AzureIntegration.
type AzureIntegration struct {
// ClientId Azure AD application (client) ID
ClientId string `json:"client_id"`
// Enabled Whether the integration is enabled
Enabled bool `json:"enabled"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes []string `json:"group_prefixes"`
// Host Azure host domain for the Graph API
Host string `json:"host"`
// Id The unique identifier for the integration
Id int64 `json:"id"`
// LastSyncedAt Timestamp of the last synchronization
LastSyncedAt time.Time `json:"last_synced_at"`
// SyncInterval Sync interval in seconds
SyncInterval int `json:"sync_interval"`
// TenantId Azure AD tenant ID
TenantId string `json:"tenant_id"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes []string `json:"user_group_prefixes"`
}
// BearerAuthConfig defines model for BearerAuthConfig.
type BearerAuthConfig struct {
// DistributionGroups List of group IDs that can use bearer auth
@@ -1542,6 +1624,51 @@ type Country struct {
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
type CountryCode = string
// CreateAzureIntegrationRequest defines model for CreateAzureIntegrationRequest.
type CreateAzureIntegrationRequest struct {
// ClientId Azure AD application (client) ID
ClientId string `json:"client_id"`
// ClientSecret Base64-encoded Azure AD client secret
ClientSecret string `json:"client_secret"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// Host Azure host domain for the Graph API
Host CreateAzureIntegrationRequestHost `json:"host"`
// SyncInterval Sync interval in seconds (minimum 300). Defaults to 300 if not specified.
SyncInterval *int `json:"sync_interval,omitempty"`
// TenantId Azure AD tenant ID
TenantId string `json:"tenant_id"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// CreateAzureIntegrationRequestHost Azure host domain for the Graph API
type CreateAzureIntegrationRequestHost string
// CreateGoogleIntegrationRequest defines model for CreateGoogleIntegrationRequest.
type CreateGoogleIntegrationRequest struct {
// CustomerId Customer ID from Google Workspace Account Settings
CustomerId string `json:"customer_id"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// ServiceAccountKey Base64-encoded Google service account key
ServiceAccountKey string `json:"service_account_key"`
// SyncInterval Sync interval in seconds (minimum 300). Defaults to 300 if not specified.
SyncInterval *int `json:"sync_interval,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// CreateIntegrationRequest Request payload for creating a new event streaming integration. Also used as the structure for the PUT request body, but not all fields are applicable for updates (see PUT operation description).
type CreateIntegrationRequest struct {
// Config Platform-specific configuration as key-value pairs. For creation, all necessary credentials and settings must be provided. For updates, provide the fields to change or the entire new configuration.
@@ -1557,7 +1684,19 @@ type CreateIntegrationRequest struct {
// CreateIntegrationRequestPlatform The event streaming platform to integrate with (e.g., "datadog", "s3", "firehose"). This field is used for creation. For updates (PUT), this field, if sent, is ignored by the backend.
type CreateIntegrationRequestPlatform string
// CreateScimIntegrationRequest Request payload for creating an SCIM IDP integration
// CreateOktaScimIntegrationRequest defines model for CreateOktaScimIntegrationRequest.
type CreateOktaScimIntegrationRequest struct {
// ConnectionName The Okta enterprise connection name on Auth0
ConnectionName string `json:"connection_name"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// CreateScimIntegrationRequest defines model for CreateScimIntegrationRequest.
type CreateScimIntegrationRequest struct {
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
@@ -1935,6 +2074,12 @@ type EDRSentinelOneResponse struct {
UpdatedAt time.Time `json:"updated_at"`
}
// EmailTarget Target configuration for email notification channels.
type EmailTarget struct {
// Emails List of email addresses to send notifications to.
Emails []openapi_types.Email `json:"emails"`
}
// ErrorResponse Standard error response. Note: The exact structure of this error response is inferred from `util.WriteErrorResponse` and `util.WriteError` usage in the provided Go code, as a specific Go struct for errors was not provided.
type ErrorResponse struct {
// Message A human-readable error message.
@@ -2007,6 +2152,30 @@ type GeoLocationCheckAction string
// GetTenantsResponse defines model for GetTenantsResponse.
type GetTenantsResponse = []TenantResponse
// GoogleIntegration defines model for GoogleIntegration.
type GoogleIntegration struct {
// CustomerId Customer ID from Google Workspace
CustomerId string `json:"customer_id"`
// Enabled Whether the integration is enabled
Enabled bool `json:"enabled"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes []string `json:"group_prefixes"`
// Id The unique identifier for the integration
Id int64 `json:"id"`
// LastSyncedAt Timestamp of the last synchronization
LastSyncedAt time.Time `json:"last_synced_at"`
// SyncInterval Sync interval in seconds
SyncInterval int `json:"sync_interval"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes []string `json:"user_group_prefixes"`
}
// Group defines model for Group.
type Group struct {
// Id Group ID
@@ -2063,6 +2232,18 @@ type GroupRequest struct {
Resources *[]Resource `json:"resources,omitempty"`
}
// HeaderAuthConfig Static header-value authentication. The proxy checks that the named header matches the configured value.
type HeaderAuthConfig struct {
// Enabled Whether header auth is enabled
Enabled bool `json:"enabled"`
// Header HTTP header name to check (e.g. "Authorization", "X-API-Key")
Header string `json:"header"`
// Value Expected header value. For Basic auth use "Basic base64(user:pass)". For Bearer use "Bearer token". Cleared in responses.
Value string `json:"value"`
}
// HuntressMatchAttributes Attribute conditions to match when approving agents
type HuntressMatchAttributes struct {
// DefenderPolicyStatus Policy status of Defender AV for Managed Antivirus.
@@ -2286,6 +2467,12 @@ type InstanceVersionInfo struct {
ManagementUpdateAvailable bool `json:"management_update_available"`
}
// IntegrationEnabled defines model for IntegrationEnabled.
type IntegrationEnabled struct {
// Enabled Whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
}
// IntegrationResponse Represents an event streaming integration.
type IntegrationResponse struct {
// AccountId The identifier of the account this integration belongs to.
@@ -2313,6 +2500,15 @@ type IntegrationResponse struct {
// IntegrationResponsePlatform The event streaming platform.
type IntegrationResponsePlatform string
// IntegrationSyncFilters defines model for IntegrationSyncFilters.
type IntegrationSyncFilters struct {
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// InvoicePDFResponse defines model for InvoicePDFResponse.
type InvoicePDFResponse struct {
// Url URL to redirect the user to invoice.
@@ -2714,6 +2910,67 @@ type NetworkTrafficUser struct {
Name string `json:"name"`
}
// NotificationChannelRequest Request body for creating or updating a notification channel.
type NotificationChannelRequest struct {
// Enabled Whether this notification channel is active.
Enabled bool `json:"enabled"`
// EventTypes List of activity event type codes this channel subscribes to.
EventTypes []NotificationEventType `json:"event_types"`
// Target Channel-specific target configuration. The shape depends on the `type` field:
// - `email`: requires an `EmailTarget` object
// - `webhook`: requires a `WebhookTarget` object
Target *NotificationChannelRequest_Target `json:"target,omitempty"`
// Type The type of notification channel.
Type NotificationChannelType `json:"type"`
}
// NotificationChannelRequest_Target Channel-specific target configuration. The shape depends on the `type` field:
// - `email`: requires an `EmailTarget` object
// - `webhook`: requires a `WebhookTarget` object
type NotificationChannelRequest_Target struct {
union json.RawMessage
}
// NotificationChannelResponse A notification channel configuration.
type NotificationChannelResponse struct {
// Enabled Whether this notification channel is active.
Enabled bool `json:"enabled"`
// EventTypes List of activity event type codes this channel subscribes to.
EventTypes []NotificationEventType `json:"event_types"`
// Id Unique identifier of the notification channel.
Id *string `json:"id,omitempty"`
// Target Channel-specific target configuration. The shape depends on the `type` field:
// - `email`: an `EmailTarget` object
// - `webhook`: a `WebhookTarget` object
Target *NotificationChannelResponse_Target `json:"target,omitempty"`
// Type The type of notification channel.
Type NotificationChannelType `json:"type"`
}
// NotificationChannelResponse_Target Channel-specific target configuration. The shape depends on the `type` field:
// - `email`: an `EmailTarget` object
// - `webhook`: a `WebhookTarget` object
type NotificationChannelResponse_Target struct {
union json.RawMessage
}
// NotificationChannelType The type of notification channel.
type NotificationChannelType string
// NotificationEventType An activity event type code. See `GET /api/integrations/notifications/types` for the full list
// of supported event types and their human-readable descriptions.
type NotificationEventType = string
// NotificationTypeEntry A map of event type codes to their human-readable descriptions.
type NotificationTypeEntry map[string]string
// OSVersionCheck Posture check for the version of operating system
type OSVersionCheck struct {
// Android Posture check for the version of operating system
@@ -2732,6 +2989,27 @@ type OSVersionCheck struct {
Windows *MinKernelVersionCheck `json:"windows,omitempty"`
}
// OktaScimIntegration defines model for OktaScimIntegration.
type OktaScimIntegration struct {
// AuthToken SCIM API token (full on creation/regeneration, masked on retrieval)
AuthToken string `json:"auth_token"`
// Enabled Whether the integration is enabled
Enabled bool `json:"enabled"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes []string `json:"group_prefixes"`
// Id The unique identifier for the integration
Id int64 `json:"id"`
// LastSyncedAt Timestamp of the last synchronization
LastSyncedAt time.Time `json:"last_synced_at"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes []string `json:"user_group_prefixes"`
}
// PINAuthConfig defines model for PINAuthConfig.
type PINAuthConfig struct {
// Enabled Whether PIN auth is enabled
@@ -3399,6 +3677,9 @@ type ProxyAccessLog struct {
// StatusCode HTTP status code returned
StatusCode int `json:"status_code"`
// SubdivisionCode First-level administrative subdivision ISO code (e.g. state/province)
SubdivisionCode *string `json:"subdivision_code,omitempty"`
// Timestamp Timestamp when the request was made
Timestamp time.Time `json:"timestamp"`
@@ -3451,6 +3732,9 @@ type ReverseProxyDomain struct {
// Id Domain ID
Id string `json:"id"`
// RequireSubdomain Whether a subdomain label is required in front of this domain. When true, the domain cannot be used bare.
RequireSubdomain *bool `json:"require_subdomain,omitempty"`
// SupportsCustomPorts Whether the cluster supports binding arbitrary TCP/UDP ports
SupportsCustomPorts *bool `json:"supports_custom_ports,omitempty"`
@@ -3575,12 +3859,12 @@ type RulePortRange struct {
Start int `json:"start"`
}
// ScimIntegration Represents a SCIM IDP integration
// ScimIntegration defines model for ScimIntegration.
type ScimIntegration struct {
// AuthToken SCIM API token (full on creation, masked otherwise)
AuthToken string `json:"auth_token"`
// Enabled Indicates whether the integration is enabled
// Enabled Whether the integration is enabled
Enabled bool `json:"enabled"`
// GroupPrefixes List of start_with string patterns for groups to sync
@@ -3592,6 +3876,9 @@ type ScimIntegration struct {
// LastSyncedAt Timestamp of when the integration was last synced
LastSyncedAt time.Time `json:"last_synced_at"`
// Prefix The connection prefix used for the SCIM provider
Prefix string `json:"prefix"`
// Provider Name of the SCIM identity provider
Provider string `json:"provider"`
@@ -3637,7 +3924,9 @@ type SentinelOneMatchAttributesNetworkStatus string
// Service defines model for Service.
type Service struct {
Auth ServiceAuthConfig `json:"auth"`
// AccessRestrictions Connection-level access restrictions based on IP address or geography. Applies to both HTTP and L4 services.
AccessRestrictions *AccessRestrictions `json:"access_restrictions,omitempty"`
Auth ServiceAuthConfig `json:"auth"`
// Domain Domain for the service
Domain string `json:"domain"`
@@ -3672,6 +3961,9 @@ type Service struct {
// Targets List of target backends for this service
Targets []ServiceTarget `json:"targets"`
// Terminated Whether the service has been terminated. Terminated services cannot be updated. Services that violate the Terms of Service will be terminated.
Terminated *bool `json:"terminated,omitempty"`
}
// ServiceMode Service mode. "http" for L7 reverse proxy, "tcp"/"udp"/"tls" for L4 passthrough.
@@ -3680,6 +3972,7 @@ type ServiceMode string
// ServiceAuthConfig defines model for ServiceAuthConfig.
type ServiceAuthConfig struct {
BearerAuth *BearerAuthConfig `json:"bearer_auth,omitempty"`
HeaderAuths *[]HeaderAuthConfig `json:"header_auths,omitempty"`
LinkAuth *LinkAuthConfig `json:"link_auth,omitempty"`
PasswordAuth *PasswordAuthConfig `json:"password_auth,omitempty"`
PinAuth *PINAuthConfig `json:"pin_auth,omitempty"`
@@ -3702,7 +3995,9 @@ type ServiceMetaStatus string
// ServiceRequest defines model for ServiceRequest.
type ServiceRequest struct {
Auth *ServiceAuthConfig `json:"auth,omitempty"`
// AccessRestrictions Connection-level access restrictions based on IP address or geography. Applies to both HTTP and L4 services.
AccessRestrictions *AccessRestrictions `json:"access_restrictions,omitempty"`
Auth *ServiceAuthConfig `json:"auth,omitempty"`
// Domain Domain for the service
Domain string `json:"domain"`
@@ -3777,7 +4072,7 @@ type ServiceTargetOptions struct {
// RequestTimeout Per-target response timeout as a Go duration string (e.g. "30s", "2m")
RequestTimeout *string `json:"request_timeout,omitempty"`
// SessionIdleTimeout Idle timeout before a UDP session is reaped, as a Go duration string (e.g. "30s", "2m"). Maximum 10m.
// SessionIdleTimeout Idle timeout before a UDP session is reaped, as a Go duration string (e.g. "30s", "2m").
SessionIdleTimeout *string `json:"session_idle_timeout,omitempty"`
// SkipTlsVerify Skip TLS certificate verification for this backend
@@ -3988,6 +4283,11 @@ type Subscription struct {
UpdatedAt time.Time `json:"updated_at"`
}
// SyncResult Response for a manual sync trigger
type SyncResult struct {
Result *string `json:"result,omitempty"`
}
// TenantGroupResponse defines model for TenantGroupResponse.
type TenantGroupResponse struct {
// Id The Group ID
@@ -4033,14 +4333,74 @@ type TenantResponse struct {
// TenantResponseStatus The status of the tenant
type TenantResponseStatus string
// UpdateScimIntegrationRequest Request payload for updating an SCIM IDP integration
type UpdateScimIntegrationRequest struct {
// Enabled Indicates whether the integration is enabled
// UpdateAzureIntegrationRequest defines model for UpdateAzureIntegrationRequest.
type UpdateAzureIntegrationRequest struct {
// ClientId Azure AD application (client) ID
ClientId *string `json:"client_id,omitempty"`
// ClientSecret Base64-encoded Azure AD client secret
ClientSecret *string `json:"client_secret,omitempty"`
// Enabled Whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// SyncInterval Sync interval in seconds (minimum 300)
SyncInterval *int `json:"sync_interval,omitempty"`
// TenantId Azure AD tenant ID
TenantId *string `json:"tenant_id,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// UpdateGoogleIntegrationRequest defines model for UpdateGoogleIntegrationRequest.
type UpdateGoogleIntegrationRequest struct {
// CustomerId Customer ID from Google Workspace Account Settings
CustomerId *string `json:"customer_id,omitempty"`
// Enabled Whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// ServiceAccountKey Base64-encoded Google service account key
ServiceAccountKey *string `json:"service_account_key,omitempty"`
// SyncInterval Sync interval in seconds (minimum 300)
SyncInterval *int `json:"sync_interval,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// UpdateOktaScimIntegrationRequest defines model for UpdateOktaScimIntegrationRequest.
type UpdateOktaScimIntegrationRequest struct {
// Enabled Whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
// UpdateScimIntegrationRequest defines model for UpdateScimIntegrationRequest.
type UpdateScimIntegrationRequest struct {
// Enabled Whether the integration is enabled
Enabled *bool `json:"enabled,omitempty"`
// GroupPrefixes List of start_with string patterns for groups to sync
GroupPrefixes *[]string `json:"group_prefixes,omitempty"`
// Prefix The connection prefix used for the SCIM provider
Prefix *string `json:"prefix,omitempty"`
// UserGroupPrefixes List of start_with string patterns for groups which users to sync
UserGroupPrefixes *[]string `json:"user_group_prefixes,omitempty"`
}
@@ -4248,6 +4608,16 @@ type UserRequest struct {
Role string `json:"role"`
}
// WebhookTarget Target configuration for webhook notification channels.
type WebhookTarget struct {
// Headers Custom HTTP headers sent with each webhook request.
// Values are write-only; in GET responses all values are masked.
Headers *map[string]string `json:"headers,omitempty"`
// Url The webhook endpoint URL to send notifications to.
Url string `json:"url"`
}
// WorkloadRequest defines model for WorkloadRequest.
type WorkloadRequest struct {
union json.RawMessage
@@ -4550,6 +4920,12 @@ 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
// UpdateAzureIntegrationJSONRequestBody defines body for UpdateAzureIntegration for application/json ContentType.
type UpdateAzureIntegrationJSONRequestBody = UpdateAzureIntegrationRequest
// PostApiIntegrationsBillingAwsMarketplaceActivateJSONRequestBody defines body for PostApiIntegrationsBillingAwsMarketplaceActivate for application/json ContentType.
type PostApiIntegrationsBillingAwsMarketplaceActivateJSONRequestBody PostApiIntegrationsBillingAwsMarketplaceActivateJSONBody
@@ -4592,6 +4968,12 @@ type CreateSentinelOneEDRIntegrationJSONRequestBody = EDRSentinelOneRequest
// UpdateSentinelOneEDRIntegrationJSONRequestBody defines body for UpdateSentinelOneEDRIntegration for application/json ContentType.
type UpdateSentinelOneEDRIntegrationJSONRequestBody = EDRSentinelOneRequest
// CreateGoogleIntegrationJSONRequestBody defines body for CreateGoogleIntegration for application/json ContentType.
type CreateGoogleIntegrationJSONRequestBody = CreateGoogleIntegrationRequest
// UpdateGoogleIntegrationJSONRequestBody defines body for UpdateGoogleIntegration for application/json ContentType.
type UpdateGoogleIntegrationJSONRequestBody = UpdateGoogleIntegrationRequest
// PostApiIntegrationsMspTenantsJSONRequestBody defines body for PostApiIntegrationsMspTenants for application/json ContentType.
type PostApiIntegrationsMspTenantsJSONRequestBody = CreateTenantRequest
@@ -4607,6 +4989,18 @@ type PostApiIntegrationsMspTenantsIdSubscriptionJSONRequestBody PostApiIntegrati
// PostApiIntegrationsMspTenantsIdUnlinkJSONRequestBody defines body for PostApiIntegrationsMspTenantsIdUnlink for application/json ContentType.
type PostApiIntegrationsMspTenantsIdUnlinkJSONRequestBody PostApiIntegrationsMspTenantsIdUnlinkJSONBody
// CreateNotificationChannelJSONRequestBody defines body for CreateNotificationChannel for application/json ContentType.
type CreateNotificationChannelJSONRequestBody = NotificationChannelRequest
// UpdateNotificationChannelJSONRequestBody defines body for UpdateNotificationChannel for application/json ContentType.
type UpdateNotificationChannelJSONRequestBody = NotificationChannelRequest
// CreateOktaScimIntegrationJSONRequestBody defines body for CreateOktaScimIntegration for application/json ContentType.
type CreateOktaScimIntegrationJSONRequestBody = CreateOktaScimIntegrationRequest
// UpdateOktaScimIntegrationJSONRequestBody defines body for UpdateOktaScimIntegration for application/json ContentType.
type UpdateOktaScimIntegrationJSONRequestBody = UpdateOktaScimIntegrationRequest
// CreateSCIMIntegrationJSONRequestBody defines body for CreateSCIMIntegration for application/json ContentType.
type CreateSCIMIntegrationJSONRequestBody = CreateScimIntegrationRequest
@@ -4703,6 +5097,130 @@ type PutApiUsersUserIdPasswordJSONRequestBody = PasswordChangeRequest
// PostApiUsersUserIdTokensJSONRequestBody defines body for PostApiUsersUserIdTokens for application/json ContentType.
type PostApiUsersUserIdTokensJSONRequestBody = PersonalAccessTokenRequest
// AsEmailTarget returns the union data inside the NotificationChannelRequest_Target as a EmailTarget
func (t NotificationChannelRequest_Target) AsEmailTarget() (EmailTarget, error) {
var body EmailTarget
err := json.Unmarshal(t.union, &body)
return body, err
}
// FromEmailTarget overwrites any union data inside the NotificationChannelRequest_Target as the provided EmailTarget
func (t *NotificationChannelRequest_Target) FromEmailTarget(v EmailTarget) error {
b, err := json.Marshal(v)
t.union = b
return err
}
// MergeEmailTarget performs a merge with any union data inside the NotificationChannelRequest_Target, using the provided EmailTarget
func (t *NotificationChannelRequest_Target) MergeEmailTarget(v EmailTarget) error {
b, err := json.Marshal(v)
if err != nil {
return err
}
merged, err := runtime.JSONMerge(t.union, b)
t.union = merged
return err
}
// AsWebhookTarget returns the union data inside the NotificationChannelRequest_Target as a WebhookTarget
func (t NotificationChannelRequest_Target) AsWebhookTarget() (WebhookTarget, error) {
var body WebhookTarget
err := json.Unmarshal(t.union, &body)
return body, err
}
// FromWebhookTarget overwrites any union data inside the NotificationChannelRequest_Target as the provided WebhookTarget
func (t *NotificationChannelRequest_Target) FromWebhookTarget(v WebhookTarget) error {
b, err := json.Marshal(v)
t.union = b
return err
}
// MergeWebhookTarget performs a merge with any union data inside the NotificationChannelRequest_Target, using the provided WebhookTarget
func (t *NotificationChannelRequest_Target) MergeWebhookTarget(v WebhookTarget) error {
b, err := json.Marshal(v)
if err != nil {
return err
}
merged, err := runtime.JSONMerge(t.union, b)
t.union = merged
return err
}
func (t NotificationChannelRequest_Target) MarshalJSON() ([]byte, error) {
b, err := t.union.MarshalJSON()
return b, err
}
func (t *NotificationChannelRequest_Target) UnmarshalJSON(b []byte) error {
err := t.union.UnmarshalJSON(b)
return err
}
// AsEmailTarget returns the union data inside the NotificationChannelResponse_Target as a EmailTarget
func (t NotificationChannelResponse_Target) AsEmailTarget() (EmailTarget, error) {
var body EmailTarget
err := json.Unmarshal(t.union, &body)
return body, err
}
// FromEmailTarget overwrites any union data inside the NotificationChannelResponse_Target as the provided EmailTarget
func (t *NotificationChannelResponse_Target) FromEmailTarget(v EmailTarget) error {
b, err := json.Marshal(v)
t.union = b
return err
}
// MergeEmailTarget performs a merge with any union data inside the NotificationChannelResponse_Target, using the provided EmailTarget
func (t *NotificationChannelResponse_Target) MergeEmailTarget(v EmailTarget) error {
b, err := json.Marshal(v)
if err != nil {
return err
}
merged, err := runtime.JSONMerge(t.union, b)
t.union = merged
return err
}
// AsWebhookTarget returns the union data inside the NotificationChannelResponse_Target as a WebhookTarget
func (t NotificationChannelResponse_Target) AsWebhookTarget() (WebhookTarget, error) {
var body WebhookTarget
err := json.Unmarshal(t.union, &body)
return body, err
}
// FromWebhookTarget overwrites any union data inside the NotificationChannelResponse_Target as the provided WebhookTarget
func (t *NotificationChannelResponse_Target) FromWebhookTarget(v WebhookTarget) error {
b, err := json.Marshal(v)
t.union = b
return err
}
// MergeWebhookTarget performs a merge with any union data inside the NotificationChannelResponse_Target, using the provided WebhookTarget
func (t *NotificationChannelResponse_Target) MergeWebhookTarget(v WebhookTarget) error {
b, err := json.Marshal(v)
if err != nil {
return err
}
merged, err := runtime.JSONMerge(t.union, b)
t.union = merged
return err
}
func (t NotificationChannelResponse_Target) MarshalJSON() ([]byte, error) {
b, err := t.union.MarshalJSON()
return b, err
}
func (t *NotificationChannelResponse_Target) UnmarshalJSON(b []byte) error {
err := t.union.UnmarshalJSON(b)
return err
}
// AsBundleWorkloadRequest returns the union data inside the WorkloadRequest as a BundleWorkloadRequest
func (t WorkloadRequest) AsBundleWorkloadRequest() (BundleWorkloadRequest, error) {
var body BundleWorkloadRequest
File diff suppressed because it is too large Load Diff
@@ -31,6 +31,9 @@ service ProxyService {
message ProxyCapabilities {
// Whether the proxy can bind arbitrary ports for TCP/UDP/TLS services.
optional bool supports_custom_ports = 1;
// Whether the proxy requires a subdomain label in front of its cluster domain.
// When true, accounts cannot use the cluster domain bare.
optional bool require_subdomain = 2;
}
// GetMappingUpdateRequest is sent to initialise a mapping stream.
@@ -80,12 +83,27 @@ message PathMapping {
PathTargetOptions options = 3;
}
message HeaderAuth {
// Header name to check, e.g. "Authorization", "X-API-Key".
string header = 1;
// argon2id hash of the expected full header value.
string hashed_value = 2;
}
message Authentication {
string session_key = 1;
int64 max_session_age_seconds = 2;
bool password = 3;
bool pin = 4;
bool oidc = 5;
repeated HeaderAuth header_auths = 6;
}
message AccessRestrictions {
repeated string allowed_cidrs = 1;
repeated string blocked_cidrs = 2;
repeated string allowed_countries = 3;
repeated string blocked_countries = 4;
}
message ProxyMapping {
@@ -106,6 +124,7 @@ message ProxyMapping {
string mode = 10;
// For L4/TLS: the port the proxy listens on.
int32 listen_port = 11;
AccessRestrictions access_restrictions = 12;
}
// SendAccessLogRequest consists of one or more AccessLogs from a Proxy.
@@ -141,9 +160,15 @@ message AuthenticateRequest {
oneof request {
PasswordRequest password = 3;
PinRequest pin = 4;
HeaderAuthRequest header_auth = 5;
}
}
message HeaderAuthRequest {
string header_value = 1;
string header_name = 2;
}
message PasswordRequest {
string password = 1;
}
+2 -2
View File
@@ -65,8 +65,8 @@ func (b *earlyMsgBuffer) put(peerID messages.PeerID, msg Msg) bool {
}
entry := earlyMsg{
peerID: peerID,
msg: msg,
peerID: peerID,
msg: msg,
createdAt: time.Now(),
}
elem := b.order.PushBack(entry)