diff --git a/ee/localserver/presence-detection-middleware_test.go b/ee/localserver/presence-detection-middleware_test.go index c6206dcff..6fcb8c88e 100644 --- a/ee/localserver/presence-detection-middleware_test.go +++ b/ee/localserver/presence-detection-middleware_test.go @@ -79,7 +79,7 @@ func TestPresenceDetectionHandler(t *testing.T) { mockPresenceDetector := mocks.NewPresenceDetector(t) if tt.expectDetectPresenceCall { - mockPresenceDetector.On("DetectPresence", mock.AnythingOfType("string"), mock.AnythingOfType("Duration")).Return(tt.durationSinceLastDetection, tt.presenceDetectionError) + mockPresenceDetector.On("DetectPresence", mock.AnythingOfType("string"), mock.AnythingOfType("Duration")).Return(tt.durationSinceLastDetection, tt.presenceDetectionError).Once() } server := &localServer{ @@ -112,6 +112,14 @@ func TestPresenceDetectionHandler(t *testing.T) { require.NotEmpty(t, rr.Header().Get(kolideDurationSinceLastPresenceDetectionHeaderKey)) } require.Equal(t, tt.expectedStatusCode, rr.Code) + + // fire the request one more time, it should not call presence detector again + // because it's less than the minimum interval + mockPresenceDetector = mocks.NewPresenceDetector(t) + server.presenceDetector = mockPresenceDetector + + handlerToTest.ServeHTTP(rr, req) + mockPresenceDetector.AssertNotCalled(t, "DetectPresence", mock.Anything, mock.Anything) }) } } diff --git a/ee/localserver/server.go b/ee/localserver/server.go index ce4f6a917..72738ed89 100644 --- a/ee/localserver/server.go +++ b/ee/localserver/server.go @@ -60,8 +60,9 @@ type localServer struct { serverKey *rsa.PublicKey serverEcKey *ecdsa.PublicKey - presenceDetector presenceDetector - presenceDetectionMutex sync.Mutex + presenceDetector presenceDetector + presenceDetectionMutex sync.Mutex + lastPresenceDetectionAttempt time.Time } const ( @@ -422,6 +423,22 @@ func (ls *localServer) presenceDetectionHandler(next http.Handler) http.Handler ls.presenceDetectionMutex.Lock() defer ls.presenceDetectionMutex.Unlock() + // if the user fails or cancels the presence detection, we want to wait a bit before trying again + // so that if there are several queued up requests, we don't prompt the user multiple times in a row + // if they keep hitting cancel + + const minTimeBetweenPresenceDetection = 3 * time.Second + if time.Since(ls.lastPresenceDetectionAttempt) < minTimeBetweenPresenceDetection { + ls.slogger.Log(r.Context(), slog.LevelInfo, + "presence detection attempted too soon", + "min_interval", minTimeBetweenPresenceDetection, + ) + + w.Header().Add(kolideDurationSinceLastPresenceDetectionHeaderKey, presencedetection.DetectionFailedDurationValue.String()) + next.ServeHTTP(w, r) + return + } + // can test this by adding an unauthed endpoint to the mux and running, for example: // curl -i -H "X-Kolide-Presence-Detection-Interval: 10s" -H "X-Kolide-Presence-Detection-Reason: my reason" localhost:12519/id detectionIntervalStr := r.Header.Get(kolidePresenceDetectionIntervalHeaderKey) @@ -461,7 +478,6 @@ func (ls *localServer) presenceDetectionHandler(next http.Handler) http.Handler } durationSinceLastDetection, err := ls.presenceDetector.DetectPresence(reason, detectionIntervalDuration) - if err != nil { ls.slogger.Log(r.Context(), slog.LevelInfo, "presence_detection", @@ -472,6 +488,8 @@ func (ls *localServer) presenceDetectionHandler(next http.Handler) http.Handler ) } + ls.lastPresenceDetectionAttempt = time.Now() + // if there was an error, we still want to return a 200 status code // and send the request through // allow the server to decide what to do based on last detection duration