From 11eb725ac8c56fb2430f0b940903ccba9c15c781 Mon Sep 17 00:00:00 2001 From: Pascal Fischer <32096965+pascal-fischer@users.noreply.github.com> Date: Mon, 9 Mar 2026 14:56:46 +0100 Subject: [PATCH 1/4] [management] only count login request duration for successful logins (#5545) --- management/internals/shared/grpc/server.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/management/internals/shared/grpc/server.go b/management/internals/shared/grpc/server.go index a07cafe90..6e8358f02 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -330,13 +330,12 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S s.secretsManager.SetupRefresh(ctx, accountID, peer.ID) - if s.appMetrics != nil { - s.appMetrics.GRPCMetrics().CountSyncRequestDuration(time.Since(reqStart), accountID) - } - unlock() unlock = nil + if s.appMetrics != nil { + s.appMetrics.GRPCMetrics().CountSyncRequestDuration(time.Since(reqStart), accountID) + } log.WithContext(ctx).Debugf("Sync took %s", time.Since(reqStart)) s.syncSem.Add(-1) @@ -743,13 +742,6 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto log.WithContext(ctx).Debugf("Login request from peer [%s] [%s]", req.WgPubKey, sRealIP) - defer func() { - if s.appMetrics != nil { - s.appMetrics.GRPCMetrics().CountLoginRequestDuration(time.Since(reqStart), accountID) - } - log.WithContext(ctx).Debugf("Login took %s", time.Since(reqStart)) - }() - if loginReq.GetMeta() == nil { msg := status.Errorf(codes.FailedPrecondition, "peer system meta has to be provided to log in. Peer %s, remote addr %s", peerKey.String(), realIP) @@ -799,6 +791,11 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto return nil, status.Errorf(codes.Internal, "failed logging in peer") } + if s.appMetrics != nil { + s.appMetrics.GRPCMetrics().CountLoginRequestDuration(time.Since(reqStart), accountID) + } + log.WithContext(ctx).Debugf("Login took %s", time.Since(reqStart)) + return &proto.EncryptedMessage{ WgPubKey: key.PublicKey().String(), Body: encryptedResp, From 15aa6bae1b393459fe2300b815be1fe69331b20e Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 9 Mar 2026 18:39:11 +0100 Subject: [PATCH 2/4] [client] Fix exit node menu not refreshing on Windows (#5553) * [client] Fix exit node menu not refreshing on Windows TrayOpenedCh is not implemented in the systray library on Windows, so exit nodes were never refreshed after the initial connect. Combined with the management sync not having populated routes yet when the Connected status fires, this caused the exit node menu to remain empty permanently after disconnect/reconnect cycles. Add a background poller on Windows that refreshes exit nodes while connected, with fast initial polling to catch routes from management sync followed by a steady 10s interval. On macOS/Linux, TrayOpenedCh continues to handle refreshes on each tray open. Also fix a data race on connectClient assignment in the server's connect() method and add nil checks in CleanState/DeleteState to prevent panics when connectClient is nil. * Remove unused exitNodeIDs * Remove unused exitNodeState struct --- client/server/server.go | 11 +- client/server/server_connect_test.go | 187 +++++++++++++++++++++++++++ client/server/state.go | 4 +- client/ui/client_ui.go | 5 +- client/ui/event_handler.go | 3 +- client/ui/network.go | 95 +++++++++----- 6 files changed, 267 insertions(+), 38 deletions(-) create mode 100644 client/server/server_connect_test.go diff --git a/client/server/server.go b/client/server/server.go index 2c7d5abc3..69d79d9cd 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -1625,9 +1625,14 @@ func (s *Server) GetFeatures(ctx context.Context, msg *proto.GetFeaturesRequest) func (s *Server) connect(ctx context.Context, config *profilemanager.Config, statusRecorder *peer.Status, doInitialAutoUpdate bool, runningChan chan struct{}) error { log.Tracef("running client connection") - s.connectClient = internal.NewConnectClient(ctx, config, statusRecorder, doInitialAutoUpdate) - s.connectClient.SetSyncResponsePersistence(s.persistSyncResponse) - if err := s.connectClient.Run(runningChan, s.logFile); err != nil { + client := internal.NewConnectClient(ctx, config, statusRecorder, doInitialAutoUpdate) + client.SetSyncResponsePersistence(s.persistSyncResponse) + + s.mutex.Lock() + s.connectClient = client + s.mutex.Unlock() + + if err := client.Run(runningChan, s.logFile); err != nil { return err } return nil diff --git a/client/server/server_connect_test.go b/client/server/server_connect_test.go new file mode 100644 index 000000000..8d31c2ae6 --- /dev/null +++ b/client/server/server_connect_test.go @@ -0,0 +1,187 @@ +package server + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/netbirdio/netbird/client/internal" + "github.com/netbirdio/netbird/client/internal/peer" + "github.com/netbirdio/netbird/client/proto" +) + +func newTestServer() *Server { + return &Server{ + rootCtx: context.Background(), + statusRecorder: peer.NewRecorder(""), + } +} + +func newDummyConnectClient(ctx context.Context) *internal.ConnectClient { + return internal.NewConnectClient(ctx, nil, nil, false) +} + +// TestConnectSetsClientWithMutex validates that connect() sets s.connectClient +// under mutex protection so concurrent readers see a consistent value. +func TestConnectSetsClientWithMutex(t *testing.T) { + s := newTestServer() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Manually simulate what connect() does (without calling Run which panics without full setup) + client := newDummyConnectClient(ctx) + + s.mutex.Lock() + s.connectClient = client + s.mutex.Unlock() + + // Verify the assignment is visible under mutex + s.mutex.Lock() + assert.Equal(t, client, s.connectClient, "connectClient should be set") + s.mutex.Unlock() +} + +// TestConcurrentConnectClientAccess validates that concurrent reads of +// s.connectClient under mutex don't race with a write. +func TestConcurrentConnectClientAccess(t *testing.T) { + s := newTestServer() + ctx := context.Background() + client := newDummyConnectClient(ctx) + + var wg sync.WaitGroup + nilCount := 0 + setCount := 0 + var mu sync.Mutex + + // Start readers + for i := 0; i < 50; i++ { + wg.Add(1) + go func() { + defer wg.Done() + s.mutex.Lock() + c := s.connectClient + s.mutex.Unlock() + + mu.Lock() + defer mu.Unlock() + if c == nil { + nilCount++ + } else { + setCount++ + } + }() + } + + // Simulate connect() writing under mutex + time.Sleep(5 * time.Millisecond) + s.mutex.Lock() + s.connectClient = client + s.mutex.Unlock() + + wg.Wait() + + assert.Equal(t, 50, nilCount+setCount, "all goroutines should complete without panic") +} + +// TestCleanupConnection_ClearsConnectClient validates that cleanupConnection +// properly nils out connectClient. +func TestCleanupConnection_ClearsConnectClient(t *testing.T) { + s := newTestServer() + _, cancel := context.WithCancel(context.Background()) + s.actCancel = cancel + + s.connectClient = newDummyConnectClient(context.Background()) + s.clientRunning = true + + err := s.cleanupConnection() + require.NoError(t, err) + + assert.Nil(t, s.connectClient, "connectClient should be nil after cleanup") +} + +// TestCleanState_NilConnectClient validates that CleanState doesn't panic +// when connectClient is nil. +func TestCleanState_NilConnectClient(t *testing.T) { + s := newTestServer() + s.connectClient = nil + s.profileManager = nil // will cause error if it tries to proceed past the nil check + + // Should not panic — the nil check should prevent calling Status() on nil + assert.NotPanics(t, func() { + _, _ = s.CleanState(context.Background(), &proto.CleanStateRequest{All: true}) + }) +} + +// TestDeleteState_NilConnectClient validates that DeleteState doesn't panic +// when connectClient is nil. +func TestDeleteState_NilConnectClient(t *testing.T) { + s := newTestServer() + s.connectClient = nil + s.profileManager = nil + + assert.NotPanics(t, func() { + _, _ = s.DeleteState(context.Background(), &proto.DeleteStateRequest{All: true}) + }) +} + +// TestDownThenUp_StaleRunningChan documents the known state issue where +// clientRunningChan from a previous connection is already closed, causing +// waitForUp() to return immediately on reconnect. +func TestDownThenUp_StaleRunningChan(t *testing.T) { + s := newTestServer() + + // Simulate state after a successful connection + s.clientRunning = true + s.clientRunningChan = make(chan struct{}) + close(s.clientRunningChan) // closed when engine started + s.clientGiveUpChan = make(chan struct{}) + s.connectClient = newDummyConnectClient(context.Background()) + + _, cancel := context.WithCancel(context.Background()) + s.actCancel = cancel + + // Simulate Down(): cleanupConnection sets connectClient = nil + s.mutex.Lock() + err := s.cleanupConnection() + s.mutex.Unlock() + require.NoError(t, err) + + // After cleanup: connectClient is nil, clientRunning still true + // (goroutine hasn't exited yet) + s.mutex.Lock() + assert.Nil(t, s.connectClient, "connectClient should be nil after cleanup") + assert.True(t, s.clientRunning, "clientRunning still true until goroutine exits") + s.mutex.Unlock() + + // waitForUp() returns immediately due to stale closed clientRunningChan + ctx, ctxCancel := context.WithTimeout(context.Background(), 2*time.Second) + defer ctxCancel() + + waitDone := make(chan error, 1) + go func() { + _, err := s.waitForUp(ctx) + waitDone <- err + }() + + select { + case err := <-waitDone: + assert.NoError(t, err, "waitForUp returns success on stale channel") + // But connectClient is still nil — this is the stale state issue + s.mutex.Lock() + assert.Nil(t, s.connectClient, "connectClient is nil despite waitForUp success") + s.mutex.Unlock() + case <-time.After(1 * time.Second): + t.Fatal("waitForUp should have returned immediately due to stale closed channel") + } +} + +// TestConnectClient_EngineNilOnFreshClient validates that a newly created +// ConnectClient has nil Engine (before Run is called). +func TestConnectClient_EngineNilOnFreshClient(t *testing.T) { + client := newDummyConnectClient(context.Background()) + assert.Nil(t, client.Engine(), "engine should be nil on fresh ConnectClient") +} diff --git a/client/server/state.go b/client/server/state.go index 1cf85cd37..8dca6bde1 100644 --- a/client/server/state.go +++ b/client/server/state.go @@ -39,7 +39,7 @@ func (s *Server) ListStates(_ context.Context, _ *proto.ListStatesRequest) (*pro // CleanState handles cleaning of states (performing cleanup operations) func (s *Server) CleanState(ctx context.Context, req *proto.CleanStateRequest) (*proto.CleanStateResponse, error) { - if s.connectClient.Status() == internal.StatusConnected || s.connectClient.Status() == internal.StatusConnecting { + if s.connectClient != nil && (s.connectClient.Status() == internal.StatusConnected || s.connectClient.Status() == internal.StatusConnecting) { return nil, status.Errorf(codes.FailedPrecondition, "cannot clean state while connecting or connected, run 'netbird down' first.") } @@ -82,7 +82,7 @@ func (s *Server) CleanState(ctx context.Context, req *proto.CleanStateRequest) ( // DeleteState handles deletion of states without cleanup func (s *Server) DeleteState(ctx context.Context, req *proto.DeleteStateRequest) (*proto.DeleteStateResponse, error) { - if s.connectClient.Status() == internal.StatusConnected || s.connectClient.Status() == internal.StatusConnecting { + if s.connectClient != nil && (s.connectClient.Status() == internal.StatusConnected || s.connectClient.Status() == internal.StatusConnecting) { return nil, status.Errorf(codes.FailedPrecondition, "cannot clean state while connecting or connected, run 'netbird down' first.") } diff --git a/client/ui/client_ui.go b/client/ui/client_ui.go index 0290e17d5..7af00cd20 100644 --- a/client/ui/client_ui.go +++ b/client/ui/client_ui.go @@ -323,7 +323,7 @@ type serviceClient struct { exitNodeMu sync.Mutex mExitNodeItems []menuHandler - exitNodeStates []exitNodeState + exitNodeRetryCancel context.CancelFunc mExitNodeDeselectAll *systray.MenuItem logFile string wLoginURL fyne.Window @@ -924,7 +924,7 @@ func (s *serviceClient) updateStatus() error { s.mDown.Enable() s.mNetworks.Enable() s.mExitNode.Enable() - go s.updateExitNodes() + s.startExitNodeRefresh() systrayIconState = true case status.Status == string(internal.StatusConnecting): s.setConnectingStatus() @@ -985,6 +985,7 @@ func (s *serviceClient) setDisconnectedStatus() { s.mUp.Enable() s.mNetworks.Disable() s.mExitNode.Disable() + s.cancelExitNodeRetry() go s.updateExitNodes() } diff --git a/client/ui/event_handler.go b/client/ui/event_handler.go index 2216c8aeb..6adf8778c 100644 --- a/client/ui/event_handler.go +++ b/client/ui/event_handler.go @@ -100,8 +100,7 @@ func (h *eventHandler) handleConnectClick() { func (h *eventHandler) handleDisconnectClick() { h.client.mDown.Disable() - - h.client.exitNodeStates = []exitNodeState{} + h.client.cancelExitNodeRetry() if h.client.connectCancel != nil { log.Debugf("cancelling ongoing connect operation") diff --git a/client/ui/network.go b/client/ui/network.go index 9a5ad7662..ed03f5ada 100644 --- a/client/ui/network.go +++ b/client/ui/network.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "runtime" - "slices" "sort" "strings" "time" @@ -34,11 +33,6 @@ const ( type filter string -type exitNodeState struct { - id string - selected bool -} - func (s *serviceClient) showNetworksUI() { s.wNetworks = s.app.NewWindow("Networks") s.wNetworks.SetOnClosed(s.cancel) @@ -335,16 +329,75 @@ func (s *serviceClient) updateNetworksBasedOnDisplayTab(tabs *container.AppTabs, s.updateNetworks(grid, f) } -func (s *serviceClient) updateExitNodes() { +// startExitNodeRefresh initiates exit node menu refresh after connecting. +// On Windows, TrayOpenedCh is not supported by the systray library, so we use +// a background poller to keep exit nodes in sync while connected. +// On macOS/Linux, TrayOpenedCh handles refreshes on each tray open. +func (s *serviceClient) startExitNodeRefresh() { + s.cancelExitNodeRetry() + + if runtime.GOOS == "windows" { + ctx, cancel := context.WithCancel(s.ctx) + s.exitNodeMu.Lock() + s.exitNodeRetryCancel = cancel + s.exitNodeMu.Unlock() + + go s.pollExitNodes(ctx) + } else { + go s.updateExitNodes() + } +} + +func (s *serviceClient) cancelExitNodeRetry() { + s.exitNodeMu.Lock() + if s.exitNodeRetryCancel != nil { + s.exitNodeRetryCancel() + s.exitNodeRetryCancel = nil + } + s.exitNodeMu.Unlock() +} + +// pollExitNodes periodically refreshes exit nodes while connected. +// Uses a short initial interval to catch routes from the management sync, +// then switches to a longer interval for ongoing updates. +func (s *serviceClient) pollExitNodes(ctx context.Context) { + // Initial fast polling to catch routes as they appear after connect. + for i := 0; i < 5; i++ { + if s.updateExitNodes() { + break + } + select { + case <-ctx.Done(): + return + case <-time.After(2 * time.Second): + } + } + + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + s.updateExitNodes() + } + } +} + +// updateExitNodes fetches exit nodes from the daemon and recreates the menu. +// Returns true if exit nodes were found. +func (s *serviceClient) updateExitNodes() bool { conn, err := s.getSrvClient(defaultFailTimeout) if err != nil { log.Errorf("get client: %v", err) - return + return false } exitNodes, err := s.getExitNodes(conn) if err != nil { log.Errorf("get exit nodes: %v", err) - return + return false } s.exitNodeMu.Lock() @@ -354,28 +407,14 @@ func (s *serviceClient) updateExitNodes() { if len(s.mExitNodeItems) > 0 { s.mExitNode.Enable() - } else { - s.mExitNode.Disable() + return true } + + s.mExitNode.Disable() + return false } func (s *serviceClient) recreateExitNodeMenu(exitNodes []*proto.Network) { - var exitNodeIDs []exitNodeState - for _, node := range exitNodes { - exitNodeIDs = append(exitNodeIDs, exitNodeState{ - id: node.ID, - selected: node.Selected, - }) - } - - sort.Slice(exitNodeIDs, func(i, j int) bool { - return exitNodeIDs[i].id < exitNodeIDs[j].id - }) - if slices.Equal(s.exitNodeStates, exitNodeIDs) { - log.Debug("Exit node menu already up to date") - return - } - for _, node := range s.mExitNodeItems { node.cancel() node.Hide() @@ -413,8 +452,6 @@ func (s *serviceClient) recreateExitNodeMenu(exitNodes []*proto.Network) { go s.handleChecked(ctx, node.ID, menuItem) } - s.exitNodeStates = exitNodeIDs - if showDeselectAll { s.mExitNode.AddSeparator() deselectAllItem := s.mExitNode.AddSubMenuItem("Deselect All", "Deselect All") From f88429982306156f9edecb3ae8046135c9f0398a Mon Sep 17 00:00:00 2001 From: Pascal Fischer <32096965+pascal-fischer@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:45:45 +0100 Subject: [PATCH 3/4] [proxy] refactor metrics and add usage logs (#5533) * **New Features** * Access logs now include bytes_upload and bytes_download (API and schemas updated, fields required). * Certificate issuance duration is now recorded as a metric. * **Refactor** * Metrics switched from Prometheus client to OpenTelemetry-backed meters; health endpoint now exposes OpenMetrics via OTLP exporter. * **Tests** * Metric tests updated to use OpenTelemetry Prometheus exporter and MeterProvider. --- .../reverseproxy/accesslogs/accesslogentry.go | 6 + proxy/internal/accesslog/logger.go | 124 ++- proxy/internal/accesslog/middleware.go | 16 + proxy/internal/accesslog/statuswriter.go | 25 +- proxy/internal/acme/manager.go | 12 +- proxy/internal/acme/manager_test.go | 4 +- proxy/internal/metrics/metrics.go | 216 +++-- proxy/internal/metrics/metrics_test.go | 20 +- proxy/server.go | 32 +- shared/management/http/api/openapi.yml | 12 + shared/management/http/api/types.gen.go | 814 +++++++++++++++++- shared/management/proto/management.pb.go | 2 +- shared/management/proto/proxy_service.pb.go | 309 +++---- shared/management/proto/proxy_service.proto | 2 + 14 files changed, 1343 insertions(+), 251 deletions(-) diff --git a/management/internals/modules/reverseproxy/accesslogs/accesslogentry.go b/management/internals/modules/reverseproxy/accesslogs/accesslogentry.go index 019cb634a..0bcc59b68 100644 --- a/management/internals/modules/reverseproxy/accesslogs/accesslogentry.go +++ b/management/internals/modules/reverseproxy/accesslogs/accesslogentry.go @@ -24,6 +24,8 @@ type AccessLogEntry struct { Reason string UserId string `gorm:"index"` AuthMethodUsed string `gorm:"index"` + BytesUpload int64 `gorm:"index"` + BytesDownload int64 `gorm:"index"` } // FromProto creates an AccessLogEntry from a proto.AccessLog @@ -39,6 +41,8 @@ func (a *AccessLogEntry) FromProto(serviceLog *proto.AccessLog) { a.UserId = serviceLog.GetUserId() a.AuthMethodUsed = serviceLog.GetAuthMechanism() a.AccountID = serviceLog.GetAccountId() + a.BytesUpload = serviceLog.GetBytesUpload() + a.BytesDownload = serviceLog.GetBytesDownload() if sourceIP := serviceLog.GetSourceIp(); sourceIP != "" { if ip, err := netip.ParseAddr(sourceIP); err == nil { @@ -101,5 +105,7 @@ func (a *AccessLogEntry) ToAPIResponse() *api.ProxyAccessLog { AuthMethodUsed: authMethod, CountryCode: countryCode, CityName: cityName, + BytesUpload: a.BytesUpload, + BytesDownload: a.BytesDownload, } } diff --git a/proxy/internal/accesslog/logger.go b/proxy/internal/accesslog/logger.go index 9e204be65..4ba5a7755 100644 --- a/proxy/internal/accesslog/logger.go +++ b/proxy/internal/accesslog/logger.go @@ -3,6 +3,7 @@ package accesslog import ( "context" "net/netip" + "sync" "time" log "github.com/sirupsen/logrus" @@ -13,6 +14,23 @@ import ( "github.com/netbirdio/netbird/shared/management/proto" ) +const ( + requestThreshold = 10000 // Log every 10k requests + bytesThreshold = 1024 * 1024 * 1024 // Log every 1GB + usageCleanupPeriod = 1 * time.Hour // Clean up stale counters every hour + usageInactiveWindow = 24 * time.Hour // Consider domain inactive if no traffic for 24 hours +) + +type domainUsage struct { + requestCount int64 + requestStartTime time.Time + + bytesTransferred int64 + bytesStartTime time.Time + + lastActivity time.Time // Track last activity for cleanup +} + type gRPCClient interface { SendAccessLog(ctx context.Context, in *proto.SendAccessLogRequest, opts ...grpc.CallOption) (*proto.SendAccessLogResponse, error) } @@ -22,6 +40,11 @@ type Logger struct { client gRPCClient logger *log.Logger trustedProxies []netip.Prefix + + usageMux sync.Mutex + domainUsage map[string]*domainUsage + + cleanupCancel context.CancelFunc } // NewLogger creates a new access log Logger. The trustedProxies parameter @@ -31,10 +54,26 @@ func NewLogger(client gRPCClient, logger *log.Logger, trustedProxies []netip.Pre if logger == nil { logger = log.StandardLogger() } - return &Logger{ + + ctx, cancel := context.WithCancel(context.Background()) + l := &Logger{ client: client, logger: logger, trustedProxies: trustedProxies, + domainUsage: make(map[string]*domainUsage), + cleanupCancel: cancel, + } + + // Start background cleanup routine + go l.cleanupStaleUsage(ctx) + + return l +} + +// Close stops the cleanup routine. Should be called during graceful shutdown. +func (l *Logger) Close() { + if l.cleanupCancel != nil { + l.cleanupCancel() } } @@ -51,6 +90,8 @@ type logEntry struct { AuthMechanism string UserId string AuthSuccess bool + BytesUpload int64 + BytesDownload int64 } func (l *Logger) log(ctx context.Context, entry logEntry) { @@ -84,6 +125,8 @@ func (l *Logger) log(ctx context.Context, entry logEntry) { AuthMechanism: entry.AuthMechanism, UserId: entry.UserId, AuthSuccess: entry.AuthSuccess, + BytesUpload: entry.BytesUpload, + BytesDownload: entry.BytesDownload, }, }); err != nil { // If it fails to send on the gRPC connection, then at least log it to the error log. @@ -103,3 +146,82 @@ func (l *Logger) log(ctx context.Context, entry logEntry) { } }() } + +// trackUsage records request and byte counts per domain, logging when thresholds are hit. +func (l *Logger) trackUsage(domain string, bytesTransferred int64) { + if domain == "" { + return + } + + l.usageMux.Lock() + defer l.usageMux.Unlock() + + now := time.Now() + usage, exists := l.domainUsage[domain] + if !exists { + usage = &domainUsage{ + requestStartTime: now, + bytesStartTime: now, + lastActivity: now, + } + l.domainUsage[domain] = usage + } + + usage.lastActivity = now + + usage.requestCount++ + if usage.requestCount >= requestThreshold { + elapsed := time.Since(usage.requestStartTime) + l.logger.WithFields(log.Fields{ + "domain": domain, + "requests": usage.requestCount, + "duration": elapsed.String(), + }).Infof("domain %s had %d requests over %s", domain, usage.requestCount, elapsed) + + usage.requestCount = 0 + usage.requestStartTime = now + } + + usage.bytesTransferred += bytesTransferred + if usage.bytesTransferred >= bytesThreshold { + elapsed := time.Since(usage.bytesStartTime) + bytesInGB := float64(usage.bytesTransferred) / (1024 * 1024 * 1024) + l.logger.WithFields(log.Fields{ + "domain": domain, + "bytes": usage.bytesTransferred, + "bytes_gb": bytesInGB, + "duration": elapsed.String(), + }).Infof("domain %s transferred %.2f GB over %s", domain, bytesInGB, elapsed) + + usage.bytesTransferred = 0 + usage.bytesStartTime = now + } +} + +// cleanupStaleUsage removes usage entries for domains that have been inactive. +func (l *Logger) cleanupStaleUsage(ctx context.Context) { + ticker := time.NewTicker(usageCleanupPeriod) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + l.usageMux.Lock() + now := time.Now() + removed := 0 + for domain, usage := range l.domainUsage { + if now.Sub(usage.lastActivity) > usageInactiveWindow { + delete(l.domainUsage, domain) + removed++ + } + } + l.usageMux.Unlock() + + if removed > 0 { + l.logger.Debugf("cleaned up %d stale domain usage entries", removed) + } + } + } +} diff --git a/proxy/internal/accesslog/middleware.go b/proxy/internal/accesslog/middleware.go index dd4798975..7368185c0 100644 --- a/proxy/internal/accesslog/middleware.go +++ b/proxy/internal/accesslog/middleware.go @@ -32,6 +32,14 @@ func (l *Logger) Middleware(next http.Handler) http.Handler { status: http.StatusOK, } + var bytesRead int64 + if r.Body != nil { + r.Body = &bodyCounter{ + ReadCloser: r.Body, + bytesRead: &bytesRead, + } + } + // Resolve the source IP using trusted proxy configuration before passing // the request on, as the proxy will modify forwarding headers. sourceIp := extractSourceIP(r, l.trustedProxies) @@ -53,6 +61,9 @@ func (l *Logger) Middleware(next http.Handler) http.Handler { host = r.Host } + bytesUpload := bytesRead + bytesDownload := sw.bytesWritten + entry := logEntry{ ID: requestID, ServiceId: capturedData.GetServiceId(), @@ -66,10 +77,15 @@ func (l *Logger) Middleware(next http.Handler) http.Handler { AuthMechanism: capturedData.GetAuthMethod(), UserId: capturedData.GetUserID(), AuthSuccess: sw.status != http.StatusUnauthorized && sw.status != http.StatusForbidden, + BytesUpload: bytesUpload, + BytesDownload: bytesDownload, } l.logger.Debugf("response: request_id=%s method=%s host=%s path=%s status=%d duration=%dms source=%s origin=%s service=%s account=%s", requestID, r.Method, host, r.URL.Path, sw.status, duration.Milliseconds(), sourceIp, capturedData.GetOrigin(), capturedData.GetServiceId(), capturedData.GetAccountId()) l.log(r.Context(), entry) + + // Track usage for cost monitoring (upload + download) by domain + l.trackUsage(host, bytesUpload+bytesDownload) }) } diff --git a/proxy/internal/accesslog/statuswriter.go b/proxy/internal/accesslog/statuswriter.go index 43cda59f9..24f7b35e9 100644 --- a/proxy/internal/accesslog/statuswriter.go +++ b/proxy/internal/accesslog/statuswriter.go @@ -1,18 +1,39 @@ package accesslog import ( + "io" + "github.com/netbirdio/netbird/proxy/internal/responsewriter" ) -// statusWriter captures the HTTP status code from WriteHeader calls. +// statusWriter captures the HTTP status code and bytes written from responses. // It embeds responsewriter.PassthroughWriter which handles all the optional // interfaces (Hijacker, Flusher, Pusher) automatically. type statusWriter struct { *responsewriter.PassthroughWriter - status int + status int + bytesWritten int64 } func (w *statusWriter) WriteHeader(status int) { w.status = status w.PassthroughWriter.WriteHeader(status) } + +func (w *statusWriter) Write(b []byte) (int, error) { + n, err := w.PassthroughWriter.Write(b) + w.bytesWritten += int64(n) + return n, err +} + +// bodyCounter wraps an io.ReadCloser and counts bytes read from the request body. +type bodyCounter struct { + io.ReadCloser + bytesRead *int64 +} + +func (bc *bodyCounter) Read(p []byte) (int, error) { + n, err := bc.ReadCloser.Read(p) + *bc.bytesRead += int64(n) + return n, err +} diff --git a/proxy/internal/acme/manager.go b/proxy/internal/acme/manager.go index d491d65a3..ebc15314b 100644 --- a/proxy/internal/acme/manager.go +++ b/proxy/internal/acme/manager.go @@ -42,6 +42,10 @@ type domainInfo struct { err string } +type metricsRecorder interface { + RecordCertificateIssuance(duration time.Duration) +} + // Manager wraps autocert.Manager with domain tracking and cross-replica // coordination via a pluggable locking strategy. The locker prevents // duplicate ACME requests when multiple replicas share a certificate cache. @@ -55,6 +59,7 @@ type Manager struct { certNotifier certificateNotifier logger *log.Logger + metrics metricsRecorder } // NewManager creates a new ACME certificate manager. The certDir is used @@ -63,7 +68,7 @@ type Manager struct { // eabKID and eabHMACKey are optional External Account Binding credentials // required for some CAs like ZeroSSL. The eabHMACKey should be the base64 // URL-encoded string provided by the CA. -func NewManager(certDir, acmeURL, eabKID, eabHMACKey string, notifier certificateNotifier, logger *log.Logger, lockMethod CertLockMethod) *Manager { +func NewManager(certDir, acmeURL, eabKID, eabHMACKey string, notifier certificateNotifier, logger *log.Logger, lockMethod CertLockMethod, metrics metricsRecorder) *Manager { if logger == nil { logger = log.StandardLogger() } @@ -73,6 +78,7 @@ func NewManager(certDir, acmeURL, eabKID, eabHMACKey string, notifier certificat domains: make(map[domain.Domain]*domainInfo), certNotifier: notifier, logger: logger, + metrics: metrics, } var eab *acme.ExternalAccountBinding @@ -161,6 +167,10 @@ func (mgr *Manager) prefetchCertificate(d domain.Domain) { return } + if mgr.metrics != nil { + mgr.metrics.RecordCertificateIssuance(elapsed) + } + mgr.setDomainState(d, domainReady, "") now := time.Now() diff --git a/proxy/internal/acme/manager_test.go b/proxy/internal/acme/manager_test.go index f7efe5933..30a27c612 100644 --- a/proxy/internal/acme/manager_test.go +++ b/proxy/internal/acme/manager_test.go @@ -10,7 +10,7 @@ import ( ) func TestHostPolicy(t *testing.T) { - mgr := NewManager(t.TempDir(), "https://acme.example.com/directory", "", "", nil, nil, "") + mgr := NewManager(t.TempDir(), "https://acme.example.com/directory", "", "", nil, nil, "", nil) mgr.AddDomain("example.com", "acc1", "rp1") // Wait for the background prefetch goroutine to finish so the temp dir @@ -70,7 +70,7 @@ func TestHostPolicy(t *testing.T) { } func TestDomainStates(t *testing.T) { - mgr := NewManager(t.TempDir(), "https://acme.example.com/directory", "", "", nil, nil, "") + mgr := NewManager(t.TempDir(), "https://acme.example.com/directory", "", "", nil, nil, "", nil) assert.Equal(t, 0, mgr.PendingCerts(), "initially zero") assert.Equal(t, 0, mgr.TotalDomains(), "initially zero domains") diff --git a/proxy/internal/metrics/metrics.go b/proxy/internal/metrics/metrics.go index 954020f77..68ff55fe5 100644 --- a/proxy/internal/metrics/metrics.go +++ b/proxy/internal/metrics/metrics.go @@ -1,64 +1,106 @@ package metrics import ( + "context" "net/http" - "strconv" + "sync" "time" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" + "go.opentelemetry.io/otel/metric" "github.com/netbirdio/netbird/proxy/internal/proxy" "github.com/netbirdio/netbird/proxy/internal/responsewriter" ) type Metrics struct { - requestsTotal prometheus.Counter - activeRequests prometheus.Gauge - configuredDomains prometheus.Gauge - pathsPerDomain *prometheus.GaugeVec - requestDuration *prometheus.HistogramVec - backendDuration *prometheus.HistogramVec + ctx context.Context + requestsTotal metric.Int64Counter + activeRequests metric.Int64UpDownCounter + configuredDomains metric.Int64UpDownCounter + totalPaths metric.Int64UpDownCounter + requestDuration metric.Int64Histogram + backendDuration metric.Int64Histogram + certificateIssueDuration metric.Int64Histogram + + mappingsMux sync.Mutex + mappingPaths map[string]int } -func New(reg prometheus.Registerer) *Metrics { - promFactory := promauto.With(reg) - return &Metrics{ - requestsTotal: promFactory.NewCounter(prometheus.CounterOpts{ - Name: "netbird_proxy_requests_total", - Help: "Total number of requests made to the netbird proxy", - }), - activeRequests: promFactory.NewGauge(prometheus.GaugeOpts{ - Name: "netbird_proxy_active_requests_count", - Help: "Current in-flight requests handled by the netbird proxy", - }), - configuredDomains: promFactory.NewGauge(prometheus.GaugeOpts{ - Name: "netbird_proxy_domains_count", - Help: "Current number of domains configured on the netbird proxy", - }), - pathsPerDomain: promFactory.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "netbird_proxy_paths_count", - Help: "Current number of paths configured on the netbird proxy labelled by domain", - }, - []string{"domain"}, - ), - requestDuration: promFactory.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "netbird_proxy_request_duration_seconds", - Help: "Duration of requests made to the netbird proxy", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, - }, - []string{"status", "size", "method", "host", "path"}, - ), - backendDuration: promFactory.NewHistogramVec(prometheus.HistogramOpts{ - Name: "netbird_proxy_backend_duration_seconds", - Help: "Duration of peer round trip time from the netbird proxy", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, - }, - []string{"status", "size", "method", "host", "path"}, - ), +func New(ctx context.Context, meter metric.Meter) (*Metrics, error) { + requestsTotal, err := meter.Int64Counter( + "proxy.http.request.counter", + metric.WithUnit("1"), + metric.WithDescription("Total number of requests made to the netbird proxy"), + ) + if err != nil { + return nil, err } + + activeRequests, err := meter.Int64UpDownCounter( + "proxy.http.active_requests", + metric.WithUnit("1"), + metric.WithDescription("Current in-flight requests handled by the netbird proxy"), + ) + if err != nil { + return nil, err + } + + configuredDomains, err := meter.Int64UpDownCounter( + "proxy.domains.count", + metric.WithUnit("1"), + metric.WithDescription("Current number of domains configured on the netbird proxy"), + ) + if err != nil { + return nil, err + } + + totalPaths, err := meter.Int64UpDownCounter( + "proxy.paths.count", + metric.WithUnit("1"), + metric.WithDescription("Total number of paths configured on the netbird proxy"), + ) + if err != nil { + return nil, err + } + + requestDuration, err := meter.Int64Histogram( + "proxy.http.request.duration.ms", + metric.WithUnit("milliseconds"), + metric.WithDescription("Duration of requests made to the netbird proxy"), + ) + if err != nil { + return nil, err + } + + backendDuration, err := meter.Int64Histogram( + "proxy.backend.duration.ms", + metric.WithUnit("milliseconds"), + metric.WithDescription("Duration of peer round trip time from the netbird proxy"), + ) + if err != nil { + return nil, err + } + + certificateIssueDuration, err := meter.Int64Histogram( + "proxy.certificate.issue.duration.ms", + metric.WithUnit("milliseconds"), + metric.WithDescription("Duration of ACME certificate issuance"), + ) + if err != nil { + return nil, err + } + + return &Metrics{ + ctx: ctx, + requestsTotal: requestsTotal, + activeRequests: activeRequests, + configuredDomains: configuredDomains, + totalPaths: totalPaths, + requestDuration: requestDuration, + backendDuration: backendDuration, + certificateIssueDuration: certificateIssueDuration, + mappingPaths: make(map[string]int), + }, nil } type responseInterceptor struct { @@ -80,23 +122,19 @@ func (w *responseInterceptor) Write(b []byte) (int, error) { func (m *Metrics) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - m.requestsTotal.Inc() - m.activeRequests.Inc() + m.requestsTotal.Add(m.ctx, 1) + m.activeRequests.Add(m.ctx, 1) interceptor := &responseInterceptor{PassthroughWriter: responsewriter.New(w)} start := time.Now() - next.ServeHTTP(interceptor, r) - duration := time.Since(start) + defer func() { + duration := time.Since(start) + m.activeRequests.Add(m.ctx, -1) + m.requestDuration.Record(m.ctx, duration.Milliseconds()) + }() - m.activeRequests.Desc() - m.requestDuration.With(prometheus.Labels{ - "status": strconv.Itoa(interceptor.status), - "size": strconv.Itoa(interceptor.size), - "method": r.Method, - "host": r.Host, - "path": r.URL.Path, - }).Observe(duration.Seconds()) + next.ServeHTTP(interceptor, r) }) } @@ -108,44 +146,52 @@ func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { func (m *Metrics) RoundTripper(next http.RoundTripper) http.RoundTripper { return roundTripperFunc(func(req *http.Request) (*http.Response, error) { - labels := prometheus.Labels{ - "method": req.Method, - "host": req.Host, - // Fill potentially empty labels with default values to avoid cardinality issues. - "path": "/", - "status": "0", - "size": "0", - } - if req.URL != nil { - labels["path"] = req.URL.Path - } - start := time.Now() res, err := next.RoundTrip(req) duration := time.Since(start) - // Not all labels will be available if there was an error. - if res != nil { - labels["status"] = strconv.Itoa(res.StatusCode) - labels["size"] = strconv.Itoa(int(res.ContentLength)) - } - - m.backendDuration.With(labels).Observe(duration.Seconds()) + m.backendDuration.Record(m.ctx, duration.Milliseconds()) return res, err }) } func (m *Metrics) AddMapping(mapping proxy.Mapping) { - m.configuredDomains.Inc() - m.pathsPerDomain.With(prometheus.Labels{ - "domain": mapping.Host, - }).Set(float64(len(mapping.Paths))) + m.mappingsMux.Lock() + defer m.mappingsMux.Unlock() + + newPathCount := len(mapping.Paths) + oldPathCount, exists := m.mappingPaths[mapping.Host] + + if !exists { + m.configuredDomains.Add(m.ctx, 1) + } + + pathDelta := newPathCount - oldPathCount + if pathDelta != 0 { + m.totalPaths.Add(m.ctx, int64(pathDelta)) + } + + m.mappingPaths[mapping.Host] = newPathCount } func (m *Metrics) RemoveMapping(mapping proxy.Mapping) { - m.configuredDomains.Dec() - m.pathsPerDomain.With(prometheus.Labels{ - "domain": mapping.Host, - }).Set(0) + m.mappingsMux.Lock() + defer m.mappingsMux.Unlock() + + oldPathCount, exists := m.mappingPaths[mapping.Host] + if !exists { + // Nothing to remove + return + } + + m.configuredDomains.Add(m.ctx, -1) + m.totalPaths.Add(m.ctx, -int64(oldPathCount)) + + delete(m.mappingPaths, mapping.Host) +} + +// RecordCertificateIssuance records the duration of a certificate issuance. +func (m *Metrics) RecordCertificateIssuance(duration time.Duration) { + m.certificateIssueDuration.Record(m.ctx, duration.Milliseconds()) } diff --git a/proxy/internal/metrics/metrics_test.go b/proxy/internal/metrics/metrics_test.go index 31e00ae64..f81072eda 100644 --- a/proxy/internal/metrics/metrics_test.go +++ b/proxy/internal/metrics/metrics_test.go @@ -1,13 +1,17 @@ package metrics_test import ( + "context" "net/http" "net/url" + "reflect" "testing" "github.com/google/go-cmp/cmp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + "github.com/netbirdio/netbird/proxy/internal/metrics" - "github.com/prometheus/client_golang/prometheus" ) type testRoundTripper struct { @@ -47,7 +51,19 @@ func TestMetrics_RoundTripper(t *testing.T) { }, } - m := metrics.New(prometheus.NewRegistry()) + exporter, err := prometheus.New() + if err != nil { + t.Fatalf("create prometheus exporter: %v", err) + } + + provider := metric.NewMeterProvider(metric.WithReader(exporter)) + pkg := reflect.TypeOf(metrics.Metrics{}).PkgPath() + meter := provider.Meter(pkg) + + m, err := metrics.New(context.Background(), meter) + if err != nil { + t.Fatalf("create metrics: %v", err) + } for name, test := range tests { t.Run(name, func(t *testing.T) { diff --git a/proxy/server.go b/proxy/server.go index 0d1aa2f6c..123b14648 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -19,14 +19,17 @@ import ( "net/netip" "net/url" "path/filepath" + "reflect" "sync" "time" "github.com/cenkalti/backoff/v4" "github.com/pires/go-proxyproto" - "github.com/prometheus/client_golang/prometheus" + prometheus2 "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" @@ -42,7 +45,7 @@ import ( proxygrpc "github.com/netbirdio/netbird/proxy/internal/grpc" "github.com/netbirdio/netbird/proxy/internal/health" "github.com/netbirdio/netbird/proxy/internal/k8s" - "github.com/netbirdio/netbird/proxy/internal/metrics" + proxymetrics "github.com/netbirdio/netbird/proxy/internal/metrics" "github.com/netbirdio/netbird/proxy/internal/proxy" "github.com/netbirdio/netbird/proxy/internal/roundtrip" "github.com/netbirdio/netbird/proxy/internal/types" @@ -63,7 +66,7 @@ type Server struct { debug *http.Server healthServer *health.Server healthChecker *health.Checker - meter *metrics.Metrics + meter *proxymetrics.Metrics // hijackTracker tracks hijacked connections (e.g. WebSocket upgrades) // so they can be closed during graceful shutdown, since http.Server.Shutdown @@ -152,8 +155,19 @@ func (s *Server) NotifyCertificateIssued(ctx context.Context, accountID, service func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) { s.initDefaults() - reg := prometheus.NewRegistry() - s.meter = metrics.New(reg) + exporter, err := prometheus.New() + if err != nil { + return fmt.Errorf("create prometheus exporter: %w", err) + } + + provider := metric.NewMeterProvider(metric.WithReader(exporter)) + pkg := reflect.TypeOf(Server{}).PkgPath() + meter := provider.Meter(pkg) + + s.meter, err = proxymetrics.New(ctx, meter) + if err != nil { + return fmt.Errorf("create metrics: %w", err) + } mgmtConn, err := s.dialManagement() if err != nil { @@ -193,7 +207,7 @@ func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) { s.startDebugEndpoint() - if err := s.startHealthServer(reg); err != nil { + if err := s.startHealthServer(); err != nil { return err } @@ -284,12 +298,12 @@ func (s *Server) startDebugEndpoint() { } // startHealthServer launches the health probe and metrics server. -func (s *Server) startHealthServer(reg *prometheus.Registry) error { +func (s *Server) startHealthServer() error { healthAddr := s.HealthAddress if healthAddr == "" { healthAddr = defaultHealthAddr } - s.healthServer = health.NewServer(healthAddr, s.healthChecker, s.Logger, promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) + s.healthServer = health.NewServer(healthAddr, s.healthChecker, s.Logger, promhttp.HandlerFor(prometheus2.DefaultGatherer, promhttp.HandlerOpts{EnableOpenMetrics: true})) healthListener, err := net.Listen("tcp", healthAddr) if err != nil { return fmt.Errorf("health probe server listen on %s: %w", healthAddr, err) @@ -423,7 +437,7 @@ func (s *Server) configureTLS(ctx context.Context) (*tls.Config, error) { "acme_server": s.ACMEDirectory, "challenge_type": s.ACMEChallengeType, }).Debug("ACME certificates enabled, configuring certificate manager") - s.acme = acme.NewManager(s.CertificateDirectory, s.ACMEDirectory, s.ACMEEABKID, s.ACMEEABHMACKey, s, s.Logger, s.CertLockMethod) + s.acme = acme.NewManager(s.CertificateDirectory, s.ACMEDirectory, s.ACMEEABKID, s.ACMEEABHMACKey, s, s.Logger, s.CertLockMethod, s.meter) if s.ACMEChallengeType == "http-01" { s.http = &http.Server{ diff --git a/shared/management/http/api/openapi.yml b/shared/management/http/api/openapi.yml index 7f03d6986..c67231342 100644 --- a/shared/management/http/api/openapi.yml +++ b/shared/management/http/api/openapi.yml @@ -2822,6 +2822,16 @@ components: type: string description: "City name from geolocation" example: "San Francisco" + bytes_upload: + type: integer + format: int64 + description: "Bytes uploaded (request body size)" + example: 1024 + bytes_download: + type: integer + format: int64 + description: "Bytes downloaded (response body size)" + example: 8192 required: - id - service_id @@ -2831,6 +2841,8 @@ components: - path - duration_ms - status_code + - bytes_upload + - bytes_download ProxyAccessLogsResponse: type: object properties: diff --git a/shared/management/http/api/types.gen.go b/shared/management/http/api/types.gen.go index d4a07f806..f218679c0 100644 --- a/shared/management/http/api/types.gen.go +++ b/shared/management/http/api/types.gen.go @@ -1,6 +1,6 @@ // Package api provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.1 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.6.0 DO NOT EDIT. package api import ( @@ -24,6 +24,22 @@ const ( CreateIntegrationRequestPlatformS3 CreateIntegrationRequestPlatform = "s3" ) +// Valid indicates whether the value is a known member of the CreateIntegrationRequestPlatform enum. +func (e CreateIntegrationRequestPlatform) Valid() bool { + switch e { + case CreateIntegrationRequestPlatformDatadog: + return true + case CreateIntegrationRequestPlatformFirehose: + return true + case CreateIntegrationRequestPlatformGenericHttp: + return true + case CreateIntegrationRequestPlatformS3: + return true + default: + return false + } +} + // Defines values for DNSRecordType. const ( DNSRecordTypeA DNSRecordType = "A" @@ -31,6 +47,20 @@ const ( DNSRecordTypeCNAME DNSRecordType = "CNAME" ) +// Valid indicates whether the value is a known member of the DNSRecordType enum. +func (e DNSRecordType) Valid() bool { + switch e { + case DNSRecordTypeA: + return true + case DNSRecordTypeAAAA: + return true + case DNSRecordTypeCNAME: + return true + default: + return false + } +} + // Defines values for EventActivityCode. const ( EventActivityCodeAccountCreate EventActivityCode = "account.create" @@ -147,12 +177,256 @@ const ( EventActivityCodeUserUnblock EventActivityCode = "user.unblock" ) +// Valid indicates whether the value is a known member of the EventActivityCode enum. +func (e EventActivityCode) Valid() bool { + switch e { + case EventActivityCodeAccountCreate: + return true + case EventActivityCodeAccountDelete: + return true + case EventActivityCodeAccountDnsDomainUpdate: + return true + case EventActivityCodeAccountNetworkRangeUpdate: + return true + case EventActivityCodeAccountPeerInactivityExpirationDisable: + return true + case EventActivityCodeAccountPeerInactivityExpirationEnable: + return true + case EventActivityCodeAccountPeerInactivityExpirationUpdate: + return true + case EventActivityCodeAccountSettingGroupPropagationDisable: + return true + case EventActivityCodeAccountSettingGroupPropagationEnable: + return true + case EventActivityCodeAccountSettingLazyConnectionDisable: + return true + case EventActivityCodeAccountSettingLazyConnectionEnable: + return true + case EventActivityCodeAccountSettingPeerApprovalDisable: + return true + case EventActivityCodeAccountSettingPeerApprovalEnable: + return true + case EventActivityCodeAccountSettingPeerLoginExpirationDisable: + return true + case EventActivityCodeAccountSettingPeerLoginExpirationEnable: + return true + case EventActivityCodeAccountSettingPeerLoginExpirationUpdate: + return true + case EventActivityCodeAccountSettingRoutingPeerDnsResolutionDisable: + return true + case EventActivityCodeAccountSettingRoutingPeerDnsResolutionEnable: + return true + case EventActivityCodeAccountSettingsAutoVersionUpdate: + return true + case EventActivityCodeDashboardLogin: + return true + case EventActivityCodeDnsSettingDisabledManagementGroupAdd: + return true + case EventActivityCodeDnsSettingDisabledManagementGroupDelete: + return true + case EventActivityCodeDnsZoneCreate: + return true + case EventActivityCodeDnsZoneDelete: + return true + case EventActivityCodeDnsZoneRecordCreate: + return true + case EventActivityCodeDnsZoneRecordDelete: + return true + case EventActivityCodeDnsZoneRecordUpdate: + return true + case EventActivityCodeDnsZoneUpdate: + return true + case EventActivityCodeGroupAdd: + return true + case EventActivityCodeGroupDelete: + return true + case EventActivityCodeGroupUpdate: + return true + case EventActivityCodeIdentityproviderCreate: + return true + case EventActivityCodeIdentityproviderDelete: + return true + case EventActivityCodeIdentityproviderUpdate: + return true + case EventActivityCodeIntegrationCreate: + return true + case EventActivityCodeIntegrationDelete: + return true + case EventActivityCodeIntegrationUpdate: + return true + case EventActivityCodeNameserverGroupAdd: + return true + case EventActivityCodeNameserverGroupDelete: + return true + case EventActivityCodeNameserverGroupUpdate: + return true + case EventActivityCodeNetworkCreate: + return true + case EventActivityCodeNetworkDelete: + return true + case EventActivityCodeNetworkResourceCreate: + return true + case EventActivityCodeNetworkResourceDelete: + return true + case EventActivityCodeNetworkResourceUpdate: + return true + case EventActivityCodeNetworkRouterCreate: + return true + case EventActivityCodeNetworkRouterDelete: + return true + case EventActivityCodeNetworkRouterUpdate: + return true + case EventActivityCodeNetworkUpdate: + return true + case EventActivityCodePeerApprovalRevoke: + return true + case EventActivityCodePeerApprove: + return true + case EventActivityCodePeerGroupAdd: + return true + case EventActivityCodePeerGroupDelete: + return true + case EventActivityCodePeerInactivityExpirationDisable: + return true + case EventActivityCodePeerInactivityExpirationEnable: + return true + case EventActivityCodePeerIpUpdate: + return true + case EventActivityCodePeerJobCreate: + return true + case EventActivityCodePeerLoginExpirationDisable: + return true + case EventActivityCodePeerLoginExpirationEnable: + return true + case EventActivityCodePeerLoginExpire: + return true + case EventActivityCodePeerRename: + return true + case EventActivityCodePeerSetupkeyAdd: + return true + case EventActivityCodePeerSshDisable: + return true + case EventActivityCodePeerSshEnable: + return true + case EventActivityCodePeerUserAdd: + return true + case EventActivityCodePersonalAccessTokenCreate: + return true + case EventActivityCodePersonalAccessTokenDelete: + return true + case EventActivityCodePolicyAdd: + return true + case EventActivityCodePolicyDelete: + return true + case EventActivityCodePolicyUpdate: + return true + case EventActivityCodePostureCheckCreate: + return true + case EventActivityCodePostureCheckDelete: + return true + case EventActivityCodePostureCheckUpdate: + return true + case EventActivityCodeResourceGroupAdd: + return true + case EventActivityCodeResourceGroupDelete: + return true + case EventActivityCodeRouteAdd: + return true + case EventActivityCodeRouteDelete: + return true + case EventActivityCodeRouteUpdate: + return true + case EventActivityCodeRuleAdd: + return true + case EventActivityCodeRuleDelete: + return true + case EventActivityCodeRuleUpdate: + return true + case EventActivityCodeServiceCreate: + return true + case EventActivityCodeServiceDelete: + return true + case EventActivityCodeServiceUpdate: + return true + case EventActivityCodeServiceUserCreate: + return true + case EventActivityCodeServiceUserDelete: + return true + case EventActivityCodeSetupkeyAdd: + return true + case EventActivityCodeSetupkeyDelete: + return true + case EventActivityCodeSetupkeyGroupAdd: + return true + case EventActivityCodeSetupkeyGroupDelete: + return true + case EventActivityCodeSetupkeyOveruse: + return true + case EventActivityCodeSetupkeyRevoke: + return true + case EventActivityCodeSetupkeyUpdate: + return true + case EventActivityCodeTransferredOwnerRole: + return true + case EventActivityCodeUserApprove: + return true + case EventActivityCodeUserBlock: + return true + case EventActivityCodeUserCreate: + return true + case EventActivityCodeUserDelete: + return true + case EventActivityCodeUserGroupAdd: + return true + case EventActivityCodeUserGroupDelete: + return true + case EventActivityCodeUserInvite: + return true + case EventActivityCodeUserInviteLinkAccept: + return true + case EventActivityCodeUserInviteLinkCreate: + return true + case EventActivityCodeUserInviteLinkDelete: + return true + case EventActivityCodeUserInviteLinkRegenerate: + return true + case EventActivityCodeUserJoin: + return true + case EventActivityCodeUserPasswordChange: + return true + case EventActivityCodeUserPeerDelete: + return true + case EventActivityCodeUserPeerLogin: + return true + case EventActivityCodeUserReject: + return true + case EventActivityCodeUserRoleUpdate: + return true + case EventActivityCodeUserUnblock: + return true + default: + return false + } +} + // Defines values for GeoLocationCheckAction. const ( GeoLocationCheckActionAllow GeoLocationCheckAction = "allow" GeoLocationCheckActionDeny GeoLocationCheckAction = "deny" ) +// Valid indicates whether the value is a known member of the GeoLocationCheckAction enum. +func (e GeoLocationCheckAction) Valid() bool { + switch e { + case GeoLocationCheckActionAllow: + return true + case GeoLocationCheckActionDeny: + return true + default: + return false + } +} + // Defines values for GroupIssued. const ( GroupIssuedApi GroupIssued = "api" @@ -160,6 +434,20 @@ const ( GroupIssuedJwt GroupIssued = "jwt" ) +// Valid indicates whether the value is a known member of the GroupIssued enum. +func (e GroupIssued) Valid() bool { + switch e { + case GroupIssuedApi: + return true + case GroupIssuedIntegration: + return true + case GroupIssuedJwt: + return true + default: + return false + } +} + // Defines values for GroupMinimumIssued. const ( GroupMinimumIssuedApi GroupMinimumIssued = "api" @@ -167,6 +455,20 @@ const ( GroupMinimumIssuedJwt GroupMinimumIssued = "jwt" ) +// Valid indicates whether the value is a known member of the GroupMinimumIssued enum. +func (e GroupMinimumIssued) Valid() bool { + switch e { + case GroupMinimumIssuedApi: + return true + case GroupMinimumIssuedIntegration: + return true + case GroupMinimumIssuedJwt: + return true + default: + return false + } +} + // Defines values for IdentityProviderType. const ( IdentityProviderTypeEntra IdentityProviderType = "entra" @@ -178,6 +480,28 @@ const ( IdentityProviderTypeZitadel IdentityProviderType = "zitadel" ) +// Valid indicates whether the value is a known member of the IdentityProviderType enum. +func (e IdentityProviderType) Valid() bool { + switch e { + case IdentityProviderTypeEntra: + return true + case IdentityProviderTypeGoogle: + return true + case IdentityProviderTypeMicrosoft: + return true + case IdentityProviderTypeOidc: + return true + case IdentityProviderTypeOkta: + return true + case IdentityProviderTypePocketid: + return true + case IdentityProviderTypeZitadel: + return true + default: + return false + } +} + // Defines values for IngressPortAllocationPortMappingProtocol. const ( IngressPortAllocationPortMappingProtocolTcp IngressPortAllocationPortMappingProtocol = "tcp" @@ -185,6 +509,20 @@ const ( 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" @@ -192,6 +530,20 @@ const ( 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" @@ -199,6 +551,20 @@ const ( 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" @@ -207,12 +573,40 @@ const ( IntegrationResponsePlatformS3 IntegrationResponsePlatform = "s3" ) +// Valid indicates whether the value is a known member of the IntegrationResponsePlatform enum. +func (e IntegrationResponsePlatform) Valid() bool { + switch e { + case IntegrationResponsePlatformDatadog: + return true + case IntegrationResponsePlatformFirehose: + return true + case IntegrationResponsePlatformGenericHttp: + return true + case IntegrationResponsePlatformS3: + return true + default: + return false + } +} + // Defines values for InvoiceResponseType. const ( InvoiceResponseTypeAccount InvoiceResponseType = "account" InvoiceResponseTypeTenants InvoiceResponseType = "tenants" ) +// Valid indicates whether the value is a known member of the InvoiceResponseType enum. +func (e InvoiceResponseType) Valid() bool { + switch e { + case InvoiceResponseTypeAccount: + return true + case InvoiceResponseTypeTenants: + return true + default: + return false + } +} + // Defines values for JobResponseStatus. const ( JobResponseStatusFailed JobResponseStatus = "failed" @@ -220,11 +614,35 @@ const ( JobResponseStatusSucceeded JobResponseStatus = "succeeded" ) +// Valid indicates whether the value is a known member of the JobResponseStatus enum. +func (e JobResponseStatus) Valid() bool { + switch e { + case JobResponseStatusFailed: + return true + case JobResponseStatusPending: + return true + case JobResponseStatusSucceeded: + return true + default: + return false + } +} + // Defines values for NameserverNsType. const ( NameserverNsTypeUdp NameserverNsType = "udp" ) +// Valid indicates whether the value is a known member of the NameserverNsType enum. +func (e NameserverNsType) Valid() bool { + switch e { + case NameserverNsTypeUdp: + return true + default: + return false + } +} + // Defines values for NetworkResourceType. const ( NetworkResourceTypeDomain NetworkResourceType = "domain" @@ -232,18 +650,56 @@ const ( NetworkResourceTypeSubnet NetworkResourceType = "subnet" ) +// Valid indicates whether the value is a known member of the NetworkResourceType enum. +func (e NetworkResourceType) Valid() bool { + switch e { + case NetworkResourceTypeDomain: + return true + case NetworkResourceTypeHost: + return true + case NetworkResourceTypeSubnet: + return true + default: + return false + } +} + // Defines values for PeerNetworkRangeCheckAction. const ( PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow" PeerNetworkRangeCheckActionDeny PeerNetworkRangeCheckAction = "deny" ) +// Valid indicates whether the value is a known member of the PeerNetworkRangeCheckAction enum. +func (e PeerNetworkRangeCheckAction) Valid() bool { + switch e { + case PeerNetworkRangeCheckActionAllow: + return true + case PeerNetworkRangeCheckActionDeny: + return true + default: + return false + } +} + // Defines values for PolicyRuleAction. const ( PolicyRuleActionAccept PolicyRuleAction = "accept" PolicyRuleActionDrop PolicyRuleAction = "drop" ) +// Valid indicates whether the value is a known member of the PolicyRuleAction enum. +func (e PolicyRuleAction) Valid() bool { + switch e { + case PolicyRuleActionAccept: + return true + case PolicyRuleActionDrop: + return true + default: + return false + } +} + // Defines values for PolicyRuleProtocol. const ( PolicyRuleProtocolAll PolicyRuleProtocol = "all" @@ -253,12 +709,42 @@ const ( PolicyRuleProtocolUdp PolicyRuleProtocol = "udp" ) +// Valid indicates whether the value is a known member of the PolicyRuleProtocol enum. +func (e PolicyRuleProtocol) Valid() bool { + switch e { + case PolicyRuleProtocolAll: + return true + case PolicyRuleProtocolIcmp: + return true + case PolicyRuleProtocolNetbirdSsh: + return true + case PolicyRuleProtocolTcp: + return true + case PolicyRuleProtocolUdp: + return true + default: + return false + } +} + // Defines values for PolicyRuleMinimumAction. const ( PolicyRuleMinimumActionAccept PolicyRuleMinimumAction = "accept" PolicyRuleMinimumActionDrop PolicyRuleMinimumAction = "drop" ) +// Valid indicates whether the value is a known member of the PolicyRuleMinimumAction enum. +func (e PolicyRuleMinimumAction) Valid() bool { + switch e { + case PolicyRuleMinimumActionAccept: + return true + case PolicyRuleMinimumActionDrop: + return true + default: + return false + } +} + // Defines values for PolicyRuleMinimumProtocol. const ( PolicyRuleMinimumProtocolAll PolicyRuleMinimumProtocol = "all" @@ -268,12 +754,42 @@ const ( PolicyRuleMinimumProtocolUdp PolicyRuleMinimumProtocol = "udp" ) +// Valid indicates whether the value is a known member of the PolicyRuleMinimumProtocol enum. +func (e PolicyRuleMinimumProtocol) Valid() bool { + switch e { + case PolicyRuleMinimumProtocolAll: + return true + case PolicyRuleMinimumProtocolIcmp: + return true + case PolicyRuleMinimumProtocolNetbirdSsh: + return true + case PolicyRuleMinimumProtocolTcp: + return true + case PolicyRuleMinimumProtocolUdp: + return true + default: + return false + } +} + // Defines values for PolicyRuleUpdateAction. const ( PolicyRuleUpdateActionAccept PolicyRuleUpdateAction = "accept" PolicyRuleUpdateActionDrop PolicyRuleUpdateAction = "drop" ) +// Valid indicates whether the value is a known member of the PolicyRuleUpdateAction enum. +func (e PolicyRuleUpdateAction) Valid() bool { + switch e { + case PolicyRuleUpdateActionAccept: + return true + case PolicyRuleUpdateActionDrop: + return true + default: + return false + } +} + // Defines values for PolicyRuleUpdateProtocol. const ( PolicyRuleUpdateProtocolAll PolicyRuleUpdateProtocol = "all" @@ -283,6 +799,24 @@ const ( PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp" ) +// Valid indicates whether the value is a known member of the PolicyRuleUpdateProtocol enum. +func (e PolicyRuleUpdateProtocol) Valid() bool { + switch e { + case PolicyRuleUpdateProtocolAll: + return true + case PolicyRuleUpdateProtocolIcmp: + return true + case PolicyRuleUpdateProtocolNetbirdSsh: + return true + case PolicyRuleUpdateProtocolTcp: + return true + case PolicyRuleUpdateProtocolUdp: + return true + default: + return false + } +} + // Defines values for ResourceType. const ( ResourceTypeDomain ResourceType = "domain" @@ -291,12 +825,40 @@ const ( ResourceTypeSubnet ResourceType = "subnet" ) +// Valid indicates whether the value is a known member of the ResourceType enum. +func (e ResourceType) Valid() bool { + switch e { + case ResourceTypeDomain: + return true + case ResourceTypeHost: + return true + case ResourceTypePeer: + return true + case ResourceTypeSubnet: + return true + default: + return false + } +} + // Defines values for ReverseProxyDomainType. const ( ReverseProxyDomainTypeCustom ReverseProxyDomainType = "custom" ReverseProxyDomainTypeFree ReverseProxyDomainType = "free" ) +// Valid indicates whether the value is a known member of the ReverseProxyDomainType enum. +func (e ReverseProxyDomainType) Valid() bool { + switch e { + case ReverseProxyDomainTypeCustom: + return true + case ReverseProxyDomainTypeFree: + return true + default: + return false + } +} + // Defines values for SentinelOneMatchAttributesNetworkStatus. const ( SentinelOneMatchAttributesNetworkStatusConnected SentinelOneMatchAttributesNetworkStatus = "connected" @@ -304,6 +866,20 @@ const ( SentinelOneMatchAttributesNetworkStatusQuarantined SentinelOneMatchAttributesNetworkStatus = "quarantined" ) +// Valid indicates whether the value is a known member of the SentinelOneMatchAttributesNetworkStatus enum. +func (e SentinelOneMatchAttributesNetworkStatus) Valid() bool { + switch e { + case SentinelOneMatchAttributesNetworkStatusConnected: + return true + case SentinelOneMatchAttributesNetworkStatusDisconnected: + return true + case SentinelOneMatchAttributesNetworkStatusQuarantined: + return true + default: + return false + } +} + // Defines values for ServiceMetaStatus. const ( ServiceMetaStatusActive ServiceMetaStatus = "active" @@ -314,23 +890,77 @@ const ( ServiceMetaStatusTunnelNotCreated ServiceMetaStatus = "tunnel_not_created" ) +// Valid indicates whether the value is a known member of the ServiceMetaStatus enum. +func (e ServiceMetaStatus) Valid() bool { + switch e { + case ServiceMetaStatusActive: + return true + case ServiceMetaStatusCertificateFailed: + return true + case ServiceMetaStatusCertificatePending: + return true + case ServiceMetaStatusError: + return true + case ServiceMetaStatusPending: + return true + case ServiceMetaStatusTunnelNotCreated: + return true + default: + return false + } +} + // Defines values for ServiceTargetProtocol. const ( ServiceTargetProtocolHttp ServiceTargetProtocol = "http" ServiceTargetProtocolHttps ServiceTargetProtocol = "https" ) +// Valid indicates whether the value is a known member of the ServiceTargetProtocol enum. +func (e ServiceTargetProtocol) Valid() bool { + switch e { + case ServiceTargetProtocolHttp: + return true + case ServiceTargetProtocolHttps: + return true + default: + return false + } +} + // Defines values for ServiceTargetTargetType. const ( ServiceTargetTargetTypePeer ServiceTargetTargetType = "peer" ServiceTargetTargetTypeResource ServiceTargetTargetType = "resource" ) +// Valid indicates whether the value is a known member of the ServiceTargetTargetType enum. +func (e ServiceTargetTargetType) Valid() bool { + switch e { + case ServiceTargetTargetTypePeer: + return true + case ServiceTargetTargetTypeResource: + return true + default: + return false + } +} + // Defines values for ServiceTargetOptionsPathRewrite. const ( ServiceTargetOptionsPathRewritePreserve ServiceTargetOptionsPathRewrite = "preserve" ) +// Valid indicates whether the value is a known member of the ServiceTargetOptionsPathRewrite enum. +func (e ServiceTargetOptionsPathRewrite) Valid() bool { + switch e { + case ServiceTargetOptionsPathRewritePreserve: + return true + default: + return false + } +} + // Defines values for TenantResponseStatus. const ( TenantResponseStatusActive TenantResponseStatus = "active" @@ -339,6 +969,22 @@ const ( TenantResponseStatusPending TenantResponseStatus = "pending" ) +// Valid indicates whether the value is a known member of the TenantResponseStatus enum. +func (e TenantResponseStatus) Valid() bool { + switch e { + case TenantResponseStatusActive: + return true + case TenantResponseStatusExisting: + return true + case TenantResponseStatusInvited: + return true + case TenantResponseStatusPending: + return true + default: + return false + } +} + // Defines values for UserStatus. const ( UserStatusActive UserStatus = "active" @@ -346,11 +992,35 @@ const ( UserStatusInvited UserStatus = "invited" ) +// Valid indicates whether the value is a known member of the UserStatus enum. +func (e UserStatus) Valid() bool { + switch e { + case UserStatusActive: + return true + case UserStatusBlocked: + return true + case UserStatusInvited: + return true + default: + return false + } +} + // Defines values for WorkloadType. const ( WorkloadTypeBundle WorkloadType = "bundle" ) +// Valid indicates whether the value is a known member of the WorkloadType enum. +func (e WorkloadType) Valid() bool { + switch e { + case WorkloadTypeBundle: + return true + default: + return false + } +} + // Defines values for GetApiEventsNetworkTrafficParamsType. const ( GetApiEventsNetworkTrafficParamsTypeTYPEDROP GetApiEventsNetworkTrafficParamsType = "TYPE_DROP" @@ -359,12 +1029,40 @@ const ( GetApiEventsNetworkTrafficParamsTypeTYPEUNKNOWN GetApiEventsNetworkTrafficParamsType = "TYPE_UNKNOWN" ) +// Valid indicates whether the value is a known member of the GetApiEventsNetworkTrafficParamsType enum. +func (e GetApiEventsNetworkTrafficParamsType) Valid() bool { + switch e { + case GetApiEventsNetworkTrafficParamsTypeTYPEDROP: + return true + case GetApiEventsNetworkTrafficParamsTypeTYPEEND: + return true + case GetApiEventsNetworkTrafficParamsTypeTYPESTART: + return true + case GetApiEventsNetworkTrafficParamsTypeTYPEUNKNOWN: + return true + default: + return false + } +} + // Defines values for GetApiEventsNetworkTrafficParamsConnectionType. const ( GetApiEventsNetworkTrafficParamsConnectionTypeP2P GetApiEventsNetworkTrafficParamsConnectionType = "P2P" GetApiEventsNetworkTrafficParamsConnectionTypeROUTED GetApiEventsNetworkTrafficParamsConnectionType = "ROUTED" ) +// Valid indicates whether the value is a known member of the GetApiEventsNetworkTrafficParamsConnectionType enum. +func (e GetApiEventsNetworkTrafficParamsConnectionType) Valid() bool { + switch e { + case GetApiEventsNetworkTrafficParamsConnectionTypeP2P: + return true + case GetApiEventsNetworkTrafficParamsConnectionTypeROUTED: + return true + default: + return false + } +} + // Defines values for GetApiEventsNetworkTrafficParamsDirection. const ( GetApiEventsNetworkTrafficParamsDirectionDIRECTIONUNKNOWN GetApiEventsNetworkTrafficParamsDirection = "DIRECTION_UNKNOWN" @@ -372,6 +1070,20 @@ const ( GetApiEventsNetworkTrafficParamsDirectionINGRESS GetApiEventsNetworkTrafficParamsDirection = "INGRESS" ) +// Valid indicates whether the value is a known member of the GetApiEventsNetworkTrafficParamsDirection enum. +func (e GetApiEventsNetworkTrafficParamsDirection) Valid() bool { + switch e { + case GetApiEventsNetworkTrafficParamsDirectionDIRECTIONUNKNOWN: + return true + case GetApiEventsNetworkTrafficParamsDirectionEGRESS: + return true + case GetApiEventsNetworkTrafficParamsDirectionINGRESS: + return true + default: + return false + } +} + // Defines values for GetApiEventsProxyParamsSortBy. const ( GetApiEventsProxyParamsSortByAuthMethod GetApiEventsProxyParamsSortBy = "auth_method" @@ -387,12 +1099,54 @@ const ( GetApiEventsProxyParamsSortByUserId GetApiEventsProxyParamsSortBy = "user_id" ) +// Valid indicates whether the value is a known member of the GetApiEventsProxyParamsSortBy enum. +func (e GetApiEventsProxyParamsSortBy) Valid() bool { + switch e { + case GetApiEventsProxyParamsSortByAuthMethod: + return true + case GetApiEventsProxyParamsSortByDuration: + return true + case GetApiEventsProxyParamsSortByHost: + return true + case GetApiEventsProxyParamsSortByMethod: + return true + case GetApiEventsProxyParamsSortByPath: + return true + case GetApiEventsProxyParamsSortByReason: + return true + case GetApiEventsProxyParamsSortBySourceIp: + return true + case GetApiEventsProxyParamsSortByStatusCode: + return true + case GetApiEventsProxyParamsSortByTimestamp: + return true + case GetApiEventsProxyParamsSortByUrl: + return true + case GetApiEventsProxyParamsSortByUserId: + return true + default: + return false + } +} + // Defines values for GetApiEventsProxyParamsSortOrder. const ( GetApiEventsProxyParamsSortOrderAsc GetApiEventsProxyParamsSortOrder = "asc" GetApiEventsProxyParamsSortOrderDesc GetApiEventsProxyParamsSortOrder = "desc" ) +// Valid indicates whether the value is a known member of the GetApiEventsProxyParamsSortOrder enum. +func (e GetApiEventsProxyParamsSortOrder) Valid() bool { + switch e { + case GetApiEventsProxyParamsSortOrderAsc: + return true + case GetApiEventsProxyParamsSortOrderDesc: + return true + default: + return false + } +} + // Defines values for GetApiEventsProxyParamsMethod. const ( GetApiEventsProxyParamsMethodDELETE GetApiEventsProxyParamsMethod = "DELETE" @@ -404,18 +1158,64 @@ const ( GetApiEventsProxyParamsMethodPUT GetApiEventsProxyParamsMethod = "PUT" ) +// Valid indicates whether the value is a known member of the GetApiEventsProxyParamsMethod enum. +func (e GetApiEventsProxyParamsMethod) Valid() bool { + switch e { + case GetApiEventsProxyParamsMethodDELETE: + return true + case GetApiEventsProxyParamsMethodGET: + return true + case GetApiEventsProxyParamsMethodHEAD: + return true + case GetApiEventsProxyParamsMethodOPTIONS: + return true + case GetApiEventsProxyParamsMethodPATCH: + return true + case GetApiEventsProxyParamsMethodPOST: + return true + case GetApiEventsProxyParamsMethodPUT: + return true + default: + return false + } +} + // Defines values for GetApiEventsProxyParamsStatus. const ( GetApiEventsProxyParamsStatusFailed GetApiEventsProxyParamsStatus = "failed" GetApiEventsProxyParamsStatusSuccess GetApiEventsProxyParamsStatus = "success" ) +// Valid indicates whether the value is a known member of the GetApiEventsProxyParamsStatus enum. +func (e GetApiEventsProxyParamsStatus) Valid() bool { + switch e { + case GetApiEventsProxyParamsStatusFailed: + return true + case GetApiEventsProxyParamsStatusSuccess: + return true + default: + return false + } +} + // Defines values for PutApiIntegrationsMspTenantsIdInviteJSONBodyValue. const ( PutApiIntegrationsMspTenantsIdInviteJSONBodyValueAccept PutApiIntegrationsMspTenantsIdInviteJSONBodyValue = "accept" PutApiIntegrationsMspTenantsIdInviteJSONBodyValueDecline PutApiIntegrationsMspTenantsIdInviteJSONBodyValue = "decline" ) +// Valid indicates whether the value is a known member of the PutApiIntegrationsMspTenantsIdInviteJSONBodyValue enum. +func (e PutApiIntegrationsMspTenantsIdInviteJSONBodyValue) Valid() bool { + switch e { + case PutApiIntegrationsMspTenantsIdInviteJSONBodyValueAccept: + return true + case PutApiIntegrationsMspTenantsIdInviteJSONBodyValueDecline: + return true + default: + return false + } +} + // AccessiblePeer defines model for AccessiblePeer. type AccessiblePeer struct { // CityName Commonly used English name of the city @@ -598,7 +1398,7 @@ type BundleParameters struct { // BundleResult defines model for BundleResult. type BundleResult struct { - UploadKey *string `json:"upload_key"` + UploadKey *string `json:"upload_key,omitempty"` } // BundleWorkloadRequest defines model for BundleWorkloadRequest. @@ -1406,9 +2206,9 @@ type JobRequest struct { // JobResponse defines model for JobResponse. type JobResponse struct { - CompletedAt *time.Time `json:"completed_at"` + CompletedAt *time.Time `json:"completed_at,omitempty"` CreatedAt time.Time `json:"created_at"` - FailedReason *string `json:"failed_reason"` + FailedReason *string `json:"failed_reason,omitempty"` Id string `json:"id"` Status JobResponseStatus `json:"status"` TriggeredBy string `json:"triggered_by"` @@ -2419,6 +3219,12 @@ type ProxyAccessLog struct { // AuthMethodUsed Authentication method used (e.g., password, pin, oidc) AuthMethodUsed *string `json:"auth_method_used,omitempty"` + // BytesDownload Bytes downloaded (response body size) + BytesDownload int64 `json:"bytes_download"` + + // BytesUpload Bytes uploaded (request body size) + BytesUpload int64 `json:"bytes_upload"` + // CityName City name from geolocation CityName *string `json:"city_name,omitempty"` diff --git a/shared/management/proto/management.pb.go b/shared/management/proto/management.pb.go index 97a2a4d18..2c66bb946 100644 --- a/shared/management/proto/management.pb.go +++ b/shared/management/proto/management.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v6.33.3 +// protoc v6.33.0 // source: management.proto package proto diff --git a/shared/management/proto/proxy_service.pb.go b/shared/management/proto/proxy_service.pb.go index 77c8ea4f4..275e8be37 100644 --- a/shared/management/proto/proxy_service.pb.go +++ b/shared/management/proto/proxy_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v6.33.3 +// protoc v6.33.0 // source: proxy_service.proto package proto @@ -740,6 +740,8 @@ type AccessLog struct { AuthMechanism string `protobuf:"bytes,11,opt,name=auth_mechanism,json=authMechanism,proto3" json:"auth_mechanism,omitempty"` UserId string `protobuf:"bytes,12,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` AuthSuccess bool `protobuf:"varint,13,opt,name=auth_success,json=authSuccess,proto3" json:"auth_success,omitempty"` + BytesUpload int64 `protobuf:"varint,14,opt,name=bytes_upload,json=bytesUpload,proto3" json:"bytes_upload,omitempty"` + BytesDownload int64 `protobuf:"varint,15,opt,name=bytes_download,json=bytesDownload,proto3" json:"bytes_download,omitempty"` } func (x *AccessLog) Reset() { @@ -865,6 +867,20 @@ func (x *AccessLog) GetAuthSuccess() bool { return false } +func (x *AccessLog) GetBytesUpload() int64 { + if x != nil { + return x.BytesUpload + } + return 0 +} + +func (x *AccessLog) GetBytesDownload() int64 { + if x != nil { + return x.BytesDownload + } + return 0 +} + type AuthenticateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1698,7 +1714,7 @@ var file_proxy_service_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa0, 0x03, 0x0a, 0x09, 0x41, 0x63, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xea, 0x03, 0x0a, 0x09, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 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, @@ -1724,153 +1740,158 @@ var file_proxy_service_proto_rawDesc = []byte{ 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, - 0x61, 0x75, 0x74, 0x68, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x13, - 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x64, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2a, 0x0a, - 0x03, 0x70, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x2d, 0x0a, 0x0f, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x22, 0x1e, 0x0a, 0x0a, 0x50, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x70, 0x69, 0x6e, 0x22, 0x55, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 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, 0xf3, 0x01, 0x0a, 0x17, 0x53, - 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x11, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x73, - 0x73, 0x75, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, - 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb8, 0x01, 0x0a, - 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x77, - 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x67, - 0x75, 0x61, 0x72, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, - 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x6f, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x28, 0x0a, 0x0d, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x65, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4f, - 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x61, 0x75, 0x74, 0x68, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x25, + 0x0a, 0x0e, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x62, 0x79, 0x74, 0x65, 0x73, 0x44, 0x6f, 0x77, + 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, - 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 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, 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, 0x8c, - 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, 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, 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, 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, + 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x08, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2a, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x03, + 0x70, 0x69, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2d, + 0x0a, 0x0f, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x1e, 0x0a, + 0x0a, 0x50, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, + 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x22, 0x55, 0x0a, + 0x14, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 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, 0xf3, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x2f, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x2d, 0x0a, 0x12, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x69, + 0x73, 0x73, 0x75, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x63, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x12, 0x28, + 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x65, + 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, + 0x72, 0x64, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x22, 0x6f, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x28, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, + 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x65, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 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, 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, 0x8c, 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, 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, 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, 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, - 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, 0x04, 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, 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, + 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, 0x04, 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, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, + 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, 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, 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, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 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, 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, + 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, 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 ( diff --git a/shared/management/proto/proxy_service.proto b/shared/management/proto/proxy_service.proto index be553095d..195b60f01 100644 --- a/shared/management/proto/proxy_service.proto +++ b/shared/management/proto/proxy_service.proto @@ -115,6 +115,8 @@ message AccessLog { string auth_mechanism = 11; string user_id = 12; bool auth_success = 13; + int64 bytes_upload = 14; + int64 bytes_download = 15; } message AuthenticateRequest { From 5585adce18cea6ba6e5673039b5d1f104006cedc Mon Sep 17 00:00:00 2001 From: Pascal Fischer <32096965+pascal-fischer@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:04:04 +0100 Subject: [PATCH 4/4] [management] add activity events for domains (#5548) * add activity events for domains * fix test * update activity codes * update activity codes --- .../modules/reverseproxy/domain/domain.go | 9 +++++++ .../reverseproxy/domain/manager/manager.go | 27 ++++++++++++++----- management/internals/server/modules.go | 2 +- management/server/activity/codes.go | 11 ++++++++ .../testing/testing_tools/channel/channel.go | 2 +- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/management/internals/modules/reverseproxy/domain/domain.go b/management/internals/modules/reverseproxy/domain/domain.go index da3432626..83fd669af 100644 --- a/management/internals/modules/reverseproxy/domain/domain.go +++ b/management/internals/modules/reverseproxy/domain/domain.go @@ -15,3 +15,12 @@ type Domain struct { Type Type `gorm:"-"` Validated bool } + +// EventMeta returns activity event metadata for a domain +func (d *Domain) EventMeta() map[string]any { + return map[string]any{ + "domain": d.Domain, + "target_cluster": d.TargetCluster, + "validated": d.Validated, + } +} diff --git a/management/internals/modules/reverseproxy/domain/manager/manager.go b/management/internals/modules/reverseproxy/domain/manager/manager.go index 12dd051fd..8bbc98726 100644 --- a/management/internals/modules/reverseproxy/domain/manager/manager.go +++ b/management/internals/modules/reverseproxy/domain/manager/manager.go @@ -9,6 +9,8 @@ import ( log "github.com/sirupsen/logrus" "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domain" + "github.com/netbirdio/netbird/management/server/account" + "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/permissions" "github.com/netbirdio/netbird/management/server/permissions/modules" "github.com/netbirdio/netbird/management/server/permissions/operations" @@ -36,16 +38,16 @@ type Manager struct { validator domain.Validator proxyManager proxyManager permissionsManager permissions.Manager + accountManager account.Manager } -func NewManager(store store, proxyMgr proxyManager, permissionsManager permissions.Manager) Manager { +func NewManager(store store, proxyMgr proxyManager, permissionsManager permissions.Manager, accountManager account.Manager) Manager { return Manager{ - store: store, - proxyManager: proxyMgr, - validator: domain.Validator{ - Resolver: net.DefaultResolver, - }, + store: store, + proxyManager: proxyMgr, + validator: domain.Validator{Resolver: net.DefaultResolver}, permissionsManager: permissionsManager, + accountManager: accountManager, } } @@ -136,6 +138,9 @@ func (m Manager) CreateDomain(ctx context.Context, accountID, userID, domainName if err != nil { return d, fmt.Errorf("create domain in store: %w", err) } + + m.accountManager.StoreEvent(ctx, userID, d.ID, accountID, activity.DomainAdded, d.EventMeta()) + return d, nil } @@ -148,10 +153,18 @@ func (m Manager) DeleteDomain(ctx context.Context, accountID, userID, domainID s return status.NewPermissionDeniedError() } + d, err := m.store.GetCustomDomain(ctx, accountID, domainID) + if err != nil { + return fmt.Errorf("get domain from store: %w", err) + } + if err := m.store.DeleteCustomDomain(ctx, accountID, domainID); err != nil { // TODO: check for "no records" type error. Because that is a success condition. return fmt.Errorf("delete domain from store: %w", err) } + + m.accountManager.StoreEvent(ctx, userID, domainID, accountID, activity.DomainDeleted, d.EventMeta()) + return nil } @@ -218,6 +231,8 @@ func (m Manager) ValidateDomain(ctx context.Context, accountID, userID, domainID }).WithError(err).Error("update custom domain in store") return } + + m.accountManager.StoreEvent(context.Background(), userID, domainID, accountID, activity.DomainValidated, d.EventMeta()) } else { log.WithFields(log.Fields{ "accountID": accountID, diff --git a/management/internals/server/modules.go b/management/internals/server/modules.go index 2383019e2..29a8953ac 100644 --- a/management/internals/server/modules.go +++ b/management/internals/server/modules.go @@ -210,7 +210,7 @@ func (s *BaseServer) ProxyManager() proxy.Manager { func (s *BaseServer) ReverseProxyDomainManager() *manager.Manager { return Create(s, func() *manager.Manager { - m := manager.NewManager(s.Store(), s.ProxyManager(), s.PermissionsManager()) + m := manager.NewManager(s.Store(), s.ProxyManager(), s.PermissionsManager(), s.AccountManager()) return &m }) } diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index 53cf30d4c..948d599ba 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -220,6 +220,13 @@ const ( // AccountPeerExposeDisabled indicates that a user disabled peer expose for the account AccountPeerExposeDisabled Activity = 115 + // DomainAdded indicates that a user added a custom domain + DomainAdded Activity = 118 + // DomainDeleted indicates that a user deleted a custom domain + DomainDeleted Activity = 119 + // DomainValidated indicates that a custom domain was validated + DomainValidated Activity = 120 + AccountDeleted Activity = 99999 ) @@ -364,6 +371,10 @@ var activityMap = map[Activity]Code{ AccountPeerExposeEnabled: {"Account peer expose enabled", "account.setting.peer.expose.enable"}, AccountPeerExposeDisabled: {"Account peer expose disabled", "account.setting.peer.expose.disable"}, + + DomainAdded: {"Domain added", "domain.add"}, + DomainDeleted: {"Domain deleted", "domain.delete"}, + DomainValidated: {"Domain validated", "domain.validate"}, } // StringCode returns a string code of the activity diff --git a/management/server/http/testing/testing_tools/channel/channel.go b/management/server/http/testing/testing_tools/channel/channel.go index 5e33ad652..462013963 100644 --- a/management/server/http/testing/testing_tools/channel/channel.go +++ b/management/server/http/testing/testing_tools/channel/channel.go @@ -108,7 +108,7 @@ func BuildApiBlackBoxWithDBState(t testing_tools.TB, sqlFile string, expectedPee t.Fatalf("Failed to create proxy manager: %v", err) } proxyServiceServer := nbgrpc.NewProxyServiceServer(accessLogsManager, proxyTokenStore, pkceverifierStore, nbgrpc.ProxyOIDCConfig{}, peersManager, userManager, proxyMgr) - domainManager := manager.NewManager(store, proxyMgr, permissionsManager) + domainManager := manager.NewManager(store, proxyMgr, permissionsManager, am) serviceProxyController, err := proxymanager.NewGRPCController(proxyServiceServer, noopMeter) if err != nil { t.Fatalf("Failed to create proxy controller: %v", err)