mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 03:31:50 +08:00

* Add anonymous stats and user table - anonymous users users page - add feature toggle `anonymousAccess` - remove check for enterprise for `Device-Id` header in request - add anonusers/device count to stats * promise all, review comments * make use of promise all settled * refactoring: devices instead of users * review comments, moved countdevices to httpserver * fakeAnonService for tests and generate openapi spec * do not commit openapi3 and api-merged * add openapi * Apply suggestions from code review Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * formatin * precise anon devices to avoid confusion --------- Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> Co-authored-by: jguer <me@jguer.space>
182 lines
5.6 KiB
Go
182 lines
5.6 KiB
Go
package anonimpl
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
|
"github.com/grafana/grafana/pkg/services/anonymous"
|
|
"github.com/grafana/grafana/pkg/services/anonymous/anonimpl/anonstore"
|
|
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
|
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
func TestIntegrationDeviceService_tag(t *testing.T) {
|
|
type tagReq struct {
|
|
httpReq *http.Request
|
|
kind anonymous.DeviceKind
|
|
}
|
|
testCases := []struct {
|
|
name string
|
|
req []tagReq
|
|
expectedAnonUICount int64
|
|
expectedKey string
|
|
expectedDevice *anonstore.Device
|
|
}{
|
|
{
|
|
name: "no requests",
|
|
req: []tagReq{{httpReq: &http.Request{}, kind: anonymous.AnonDeviceUI}},
|
|
},
|
|
{
|
|
name: "missing info should not tag",
|
|
req: []tagReq{{httpReq: &http.Request{
|
|
Header: http.Header{
|
|
"User-Agent": []string{"test"},
|
|
},
|
|
},
|
|
kind: anonymous.AnonDeviceUI,
|
|
}},
|
|
},
|
|
{
|
|
name: "should tag device ID once",
|
|
req: []tagReq{{httpReq: &http.Request{
|
|
Header: http.Header{
|
|
"User-Agent": []string{"test"},
|
|
"X-Forwarded-For": []string{"10.30.30.1"},
|
|
http.CanonicalHeaderKey(deviceIDHeader): []string{"32mdo31deeqwes"},
|
|
},
|
|
},
|
|
kind: anonymous.AnonDeviceUI,
|
|
},
|
|
},
|
|
expectedAnonUICount: 1,
|
|
expectedKey: "ui-anon-session:32mdo31deeqwes",
|
|
expectedDevice: &anonstore.Device{
|
|
DeviceID: "32mdo31deeqwes",
|
|
ClientIP: "10.30.30.1",
|
|
UserAgent: "test"},
|
|
},
|
|
{
|
|
name: "repeat request should not tag",
|
|
req: []tagReq{{httpReq: &http.Request{
|
|
Header: http.Header{
|
|
"User-Agent": []string{"test"},
|
|
http.CanonicalHeaderKey(deviceIDHeader): []string{"32mdo31deeqwes"},
|
|
"X-Forwarded-For": []string{"10.30.30.1"},
|
|
},
|
|
},
|
|
kind: anonymous.AnonDeviceUI,
|
|
}, {httpReq: &http.Request{
|
|
Header: http.Header{
|
|
"User-Agent": []string{"test"},
|
|
http.CanonicalHeaderKey(deviceIDHeader): []string{"32mdo31deeqwes"},
|
|
"X-Forwarded-For": []string{"10.30.30.1"},
|
|
},
|
|
},
|
|
kind: anonymous.AnonDeviceUI,
|
|
},
|
|
},
|
|
expectedAnonUICount: 1,
|
|
}, {
|
|
name: "tag 2 different requests",
|
|
req: []tagReq{{httpReq: &http.Request{
|
|
Header: http.Header{
|
|
http.CanonicalHeaderKey("User-Agent"): []string{"test"},
|
|
http.CanonicalHeaderKey("X-Forwarded-For"): []string{"10.30.30.1"},
|
|
http.CanonicalHeaderKey(deviceIDHeader): []string{"a"},
|
|
},
|
|
},
|
|
kind: anonymous.AnonDeviceUI,
|
|
}, {httpReq: &http.Request{
|
|
Header: http.Header{
|
|
"User-Agent": []string{"test"},
|
|
"X-Forwarded-For": []string{"10.30.30.2"},
|
|
http.CanonicalHeaderKey(deviceIDHeader): []string{"b"},
|
|
},
|
|
},
|
|
kind: anonymous.AnonDeviceUI,
|
|
},
|
|
},
|
|
expectedAnonUICount: 2,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
store := db.InitTestDB(t)
|
|
anonDBStore := anonstore.ProvideAnonDBStore(store)
|
|
anonService := ProvideAnonymousDeviceService(&usagestats.UsageStatsMock{},
|
|
&authntest.FakeService{}, anonDBStore, setting.NewCfg(), orgtest.NewOrgServiceFake(), nil, actest.FakeAccessControl{}, &routing.RouteRegisterImpl{})
|
|
|
|
for _, req := range tc.req {
|
|
err := anonService.TagDevice(context.Background(), req.httpReq, req.kind)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
devices, err := anonDBStore.ListDevices(context.Background(), nil, nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, devices, int(tc.expectedAnonUICount))
|
|
if tc.expectedDevice != nil {
|
|
device := devices[0]
|
|
assert.NotZero(t, device.ID)
|
|
assert.NotZero(t, device.CreatedAt)
|
|
assert.NotZero(t, device.UpdatedAt)
|
|
|
|
tc.expectedDevice.ID = device.ID
|
|
tc.expectedDevice.CreatedAt = device.CreatedAt
|
|
tc.expectedDevice.UpdatedAt = device.UpdatedAt
|
|
|
|
assert.Equal(t, tc.expectedDevice, devices[0])
|
|
}
|
|
|
|
stats, err := anonService.usageStatFn(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tc.expectedAnonUICount, stats["stats.anonymous.device.ui.count"].(int64), stats)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Ensure that the local cache prevents request from being tagged
|
|
func TestIntegrationAnonDeviceService_localCacheSafety(t *testing.T) {
|
|
store := db.InitTestDB(t)
|
|
anonDBStore := anonstore.ProvideAnonDBStore(store)
|
|
anonService := ProvideAnonymousDeviceService(&usagestats.UsageStatsMock{},
|
|
&authntest.FakeService{}, anonDBStore, setting.NewCfg(), orgtest.NewOrgServiceFake(), nil, actest.FakeAccessControl{}, &routing.RouteRegisterImpl{})
|
|
|
|
req := &http.Request{
|
|
Header: http.Header{
|
|
"User-Agent": []string{"test"},
|
|
"X-Forwarded-For": []string{"10.30.30.2"},
|
|
http.CanonicalHeaderKey(deviceIDHeader): []string{"32mdo31deeqwes"},
|
|
},
|
|
}
|
|
|
|
anonDevice := &anonstore.Device{
|
|
DeviceID: "32mdo31deeqwes",
|
|
ClientIP: "10.30.30.2",
|
|
UserAgent: "test",
|
|
UpdatedAt: time.Now().UTC(),
|
|
}
|
|
|
|
key := anonDevice.CacheKey()
|
|
anonService.localCache.SetDefault(key, true)
|
|
|
|
err := anonService.TagDevice(context.Background(), req, anonymous.AnonDeviceUI)
|
|
require.NoError(t, err)
|
|
|
|
stats, err := anonService.usageStatFn(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, int64(0), stats["stats.anonymous.device.ui.count"].(int64))
|
|
}
|