mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Authn: Anon session service (#63052)
* add anon sessions package * add usage stat fn * implement count for cache * add anonservice to authn broker * lint * add tests for remote cache count * move anon service to services * wrap tagging in goroutine * make func used
This commit is contained in:
parent
56c8661929
commit
ff78103a24
9
.github/CODEOWNERS
vendored
9
.github/CODEOWNERS
vendored
@ -518,17 +518,18 @@ lerna.json @grafana/frontend-ops
|
||||
# Grafana authentication and authorization
|
||||
/pkg/login/ @grafana/grafana-authnz-team
|
||||
/pkg/services/accesscontrol/ @grafana/grafana-authnz-team
|
||||
/pkg/services/anonymous/ @grafana/grafana-authnz-team
|
||||
/pkg/services/auth/ @grafana/grafana-authnz-team
|
||||
/pkg/services/authn/ @grafana/grafana-authnz-team
|
||||
/pkg/services/dashboards/accesscontrol.go @grafana/grafana-authnz-team
|
||||
/pkg/services/datasources/permissions/ @grafana/grafana-authnz-team
|
||||
/pkg/services/guardian/ @grafana/grafana-authnz-team
|
||||
/pkg/services/ldap/ @grafana/grafana-authnz-team
|
||||
/pkg/services/login/ @grafana/grafana-authnz-team
|
||||
/pkg/services/oauthtoken/ @grafana/grafana-authnz-team
|
||||
/pkg/services/teamguardian/ @grafana/grafana-authnz-team
|
||||
/pkg/services/serviceaccounts/ @grafana/grafana-authnz-team
|
||||
/pkg/services/loginattempt/ @grafana/grafana-authnz-team
|
||||
/pkg/services/authn/ @grafana/grafana-authnz-team
|
||||
/pkg/services/oauthtoken/ @grafana/grafana-authnz-team
|
||||
/pkg/services/serviceaccounts/ @grafana/grafana-authnz-team
|
||||
/pkg/services/teamguardian/ @grafana/grafana-authnz-team
|
||||
|
||||
# Support bundles
|
||||
/public/app/features/support-bundles/ @grafana/grafana-authnz-team
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anontest"
|
||||
"github.com/grafana/grafana/pkg/services/auth/authtest"
|
||||
"github.com/grafana/grafana/pkg/services/auth/jwt"
|
||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||
@ -205,7 +206,10 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
|
||||
authProxy := authproxy.ProvideAuthProxy(cfg, remoteCacheSvc, loginservice.LoginServiceMock{}, &usertest.FakeUserService{}, sqlStore, service.NewLDAPFakeService())
|
||||
loginService := &logintest.LoginServiceFake{}
|
||||
authenticator := &logintest.AuthenticatorFake{}
|
||||
ctxHdlr := contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore, tracer, authProxy, loginService, nil, authenticator, usertest.NewUserServiceFake(), orgtest.NewOrgServiceFake(), nil, featuremgmt.WithFeatures(), &authntest.FakeService{})
|
||||
ctxHdlr := contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc,
|
||||
remoteCacheSvc, renderSvc, sqlStore, tracer, authProxy, loginService, nil,
|
||||
authenticator, usertest.NewUserServiceFake(), orgtest.NewOrgServiceFake(),
|
||||
nil, featuremgmt.WithFeatures(), &authntest.FakeService{}, &anontest.FakeAnonymousSessionService{})
|
||||
|
||||
return ctxHdlr
|
||||
}
|
||||
|
@ -148,6 +148,22 @@ func (dc *databaseCache) Delete(ctx context.Context, key string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (dc *databaseCache) Count(ctx context.Context, prefix string) (int64, error) {
|
||||
res := int64(0)
|
||||
err := dc.SQLStore.WithDbSession(ctx, func(session *db.Session) error {
|
||||
sql := "SELECT COUNT(*) FROM cache_data WHERE cache_key LIKE ?"
|
||||
|
||||
_, err := session.SQL(sql, prefix+"%").Get(&res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// CacheData is the struct representing the table in the database
|
||||
type CacheData struct {
|
||||
CacheKey string
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@ -77,3 +78,43 @@ func TestSecondSet(t *testing.T) {
|
||||
err = db.Set(context.Background(), "killa-gorilla", obj, 0)
|
||||
assert.Equal(t, err, nil)
|
||||
}
|
||||
|
||||
func TestDatabaseStorageCount(t *testing.T) {
|
||||
sqlstore := db.InitTestDB(t)
|
||||
|
||||
db := &databaseCache{
|
||||
SQLStore: sqlstore,
|
||||
codec: &gobCodec{},
|
||||
log: log.New("remotecache.database"),
|
||||
}
|
||||
|
||||
obj := &CacheableStruct{String: "foolbar"}
|
||||
|
||||
// set time.now to 2 weeks ago
|
||||
var err error
|
||||
getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) }
|
||||
err = db.Set(context.Background(), "pref-key1", obj, 1000*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.Set(context.Background(), "pref-key2", obj, 1000*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.Set(context.Background(), "pref-key3", obj, 1000*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
// insert object that should never expire
|
||||
err = db.Set(context.Background(), "pref-key4", obj, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
getTime = time.Now
|
||||
err = db.Set(context.Background(), "pref-key5", obj, 1000*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
// run GC
|
||||
db.internalRunGC()
|
||||
|
||||
// try to read values
|
||||
n, errC := db.Count(context.Background(), "pref-")
|
||||
require.NoError(t, errC)
|
||||
assert.Equal(t, int64(2), n)
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
|
||||
const memcachedCacheType = "memcached"
|
||||
|
||||
var ErrNotImplemented = errors.New("count not implemented")
|
||||
|
||||
type memcachedStorage struct {
|
||||
c *memcache.Client
|
||||
codec codec
|
||||
@ -84,6 +86,10 @@ func (s *memcachedStorage) GetByteArray(ctx context.Context, key string) ([]byte
|
||||
return memcachedItem.Value, nil
|
||||
}
|
||||
|
||||
func (s *memcachedStorage) Count(ctx context.Context, prefix string) (int64, error) {
|
||||
return 0, ErrNotImplemented
|
||||
}
|
||||
|
||||
// Delete delete a key from the cache
|
||||
func (s *memcachedStorage) Delete(ctx context.Context, key string) error {
|
||||
return s.c.Delete(key)
|
||||
|
@ -13,4 +13,5 @@ func TestMemcachedCacheStorage(t *testing.T) {
|
||||
opts := &setting.RemoteCacheOptions{Name: memcachedCacheType, ConnStr: "localhost:11211"}
|
||||
client := createTestClient(t, opts, nil)
|
||||
runTestsForClient(t, client)
|
||||
runCountTestsForClient(t, opts, nil)
|
||||
}
|
||||
|
@ -134,3 +134,12 @@ func (s *redisStorage) Delete(ctx context.Context, key string) error {
|
||||
cmd := s.c.Del(ctx, key)
|
||||
return cmd.Err()
|
||||
}
|
||||
|
||||
func (s *redisStorage) Count(ctx context.Context, prefix string) (int64, error) {
|
||||
cmd := s.c.Keys(ctx, prefix+"*")
|
||||
if cmd.Err() != nil {
|
||||
return 0, cmd.Err()
|
||||
}
|
||||
|
||||
return int64(len(cmd.Val())), nil
|
||||
}
|
||||
|
@ -14,4 +14,5 @@ func TestRedisCacheStorage(t *testing.T) {
|
||||
opts := &setting.RemoteCacheOptions{Name: redisCacheType, ConnStr: "addr=localhost:6379"}
|
||||
client := createTestClient(t, opts, nil)
|
||||
runTestsForClient(t, client)
|
||||
runCountTestsForClient(t, opts, nil)
|
||||
}
|
||||
|
@ -69,6 +69,10 @@ type CacheStorage interface {
|
||||
|
||||
// Delete object from cache
|
||||
Delete(ctx context.Context, key string) error
|
||||
|
||||
// Count returns the number of items in the cache.
|
||||
// Optionaly a prefix can be provided to only count items with that prefix
|
||||
Count(ctx context.Context, prefix string) (int64, error)
|
||||
}
|
||||
|
||||
// RemoteCache allows Grafana to cache data outside its own process
|
||||
@ -108,6 +112,11 @@ func (ds *RemoteCache) Delete(ctx context.Context, key string) error {
|
||||
return ds.client.Delete(ctx, key)
|
||||
}
|
||||
|
||||
// Count returns the number of items in the cache.
|
||||
func (ds *RemoteCache) Count(ctx context.Context, prefix string) (int64, error) {
|
||||
return ds.client.Count(ctx, prefix)
|
||||
}
|
||||
|
||||
// Run starts the backend processes for cache clients.
|
||||
func (ds *RemoteCache) Run(ctx context.Context) error {
|
||||
// create new interface if more clients need GC jobs
|
||||
@ -214,3 +223,7 @@ func (pcs *prefixCacheStorage) SetByteArray(ctx context.Context, key string, val
|
||||
func (pcs *prefixCacheStorage) Delete(ctx context.Context, key string) error {
|
||||
return pcs.cache.Delete(ctx, pcs.prefix+key)
|
||||
}
|
||||
|
||||
func (pcs *prefixCacheStorage) Count(ctx context.Context, prefix string) (int64, error) {
|
||||
return pcs.cache.Count(ctx, pcs.prefix)
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ func TestCachedBasedOnConfig(t *testing.T) {
|
||||
|
||||
client := createTestClient(t, cfg.RemoteCacheOptions, db.InitTestDB(t))
|
||||
runTestsForClient(t, client)
|
||||
runCountTestsForClient(t, cfg.RemoteCacheOptions, db.InitTestDB(t))
|
||||
}
|
||||
|
||||
func TestInvalidCacheTypeReturnsError(t *testing.T) {
|
||||
@ -56,6 +57,37 @@ func runTestsForClient(t *testing.T, client CacheStorage) {
|
||||
canNotFetchExpiredItems(t, client)
|
||||
}
|
||||
|
||||
func runCountTestsForClient(t *testing.T, opts *setting.RemoteCacheOptions, sqlstore db.DB) {
|
||||
client := createTestClient(t, opts, sqlstore)
|
||||
expectError := false
|
||||
if opts.Name == memcachedCacheType {
|
||||
expectError = true
|
||||
}
|
||||
|
||||
t.Run("can count items", func(t *testing.T) {
|
||||
cacheableStruct := CacheableStruct{String: "hej", Int64: 2000}
|
||||
|
||||
err := client.Set(context.Background(), "pref-key1", cacheableStruct, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Set(context.Background(), "pref-key2", cacheableStruct, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Set(context.Background(), "key3-not-pref", cacheableStruct, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
n, errC := client.Count(context.Background(), "pref-")
|
||||
if expectError {
|
||||
require.ErrorIs(t, ErrNotImplemented, errC)
|
||||
assert.Equal(t, int64(0), n)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, errC)
|
||||
assert.Equal(t, int64(2), n)
|
||||
})
|
||||
}
|
||||
|
||||
func canPutGetAndDeleteCachedObjects(t *testing.T, client CacheStorage) {
|
||||
cacheableStruct := CacheableStruct{String: "hej", Int64: 2000}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anontest"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/apikey/apikeytest"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
@ -959,7 +960,7 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg, mockSQLStore *dbtest.Fake
|
||||
loginService, apiKeyService, authenticator, userService, orgService,
|
||||
oauthTokenService,
|
||||
featuremgmt.WithFeatures(featuremgmt.FlagAccessTokenExpirationCheck),
|
||||
&authntest.FakeService{})
|
||||
&authntest.FakeService{}, &anontest.FakeAnonymousSessionService{})
|
||||
}
|
||||
|
||||
type fakeRenderService struct {
|
||||
|
@ -218,6 +218,7 @@ var wireBasicSet = wire.NewSet(
|
||||
wire.Bind(new(correlations.Service), new(*correlations.CorrelationsService)),
|
||||
quotaimpl.ProvideService,
|
||||
remotecache.ProvideService,
|
||||
wire.Bind(new(remotecache.CacheStorage), new(*remotecache.RemoteCache)),
|
||||
loginservice.ProvideService,
|
||||
wire.Bind(new(login.Service), new(*loginservice.Implementation)),
|
||||
authinfoservice.ProvideAuthInfoService,
|
||||
|
@ -13,6 +13,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anonimpl"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/auth/authimpl"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
@ -43,6 +45,8 @@ var wireExtsBasicSet = wire.NewSet(
|
||||
authimpl.ProvideUserAuthTokenService,
|
||||
wire.Bind(new(auth.UserTokenService), new(*authimpl.UserAuthTokenService)),
|
||||
wire.Bind(new(auth.UserTokenBackgroundService), new(*authimpl.UserAuthTokenService)),
|
||||
anonimpl.ProvideAnonymousSessionService,
|
||||
wire.Bind(new(anonymous.Service), new(*anonimpl.AnonSessionService)),
|
||||
licensing.ProvideService,
|
||||
wire.Bind(new(licensing.Licensing), new(*licensing.OSSLicensingService)),
|
||||
setting.ProvideProvider,
|
||||
|
91
pkg/services/anonymous/anonimpl/impl.go
Normal file
91
pkg/services/anonymous/anonimpl/impl.go
Normal file
@ -0,0 +1,91 @@
|
||||
package anonimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/network"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
const thirtyDays = 30 * 24 * time.Hour
|
||||
const anonCachePrefix = "anon-session"
|
||||
|
||||
type AnonSession struct {
|
||||
ip string
|
||||
userAgent string
|
||||
}
|
||||
|
||||
func (a *AnonSession) Key() (string, error) {
|
||||
key := strings.Builder{}
|
||||
key.WriteString(a.ip)
|
||||
key.WriteString(a.userAgent)
|
||||
|
||||
hash := fnv.New128a()
|
||||
if _, err := hash.Write([]byte(key.String())); err != nil {
|
||||
return "", fmt.Errorf("failed to write to hash: %w", err)
|
||||
}
|
||||
|
||||
return strings.Join([]string{anonCachePrefix, hex.EncodeToString(hash.Sum(nil))}, ":"), nil
|
||||
}
|
||||
|
||||
type AnonSessionService struct {
|
||||
remoteCache remotecache.CacheStorage
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func ProvideAnonymousSessionService(remoteCache remotecache.CacheStorage, usageStats usagestats.Service) *AnonSessionService {
|
||||
a := &AnonSessionService{
|
||||
remoteCache: remoteCache,
|
||||
log: log.New("anonymous-session-service"),
|
||||
}
|
||||
|
||||
usageStats.RegisterMetricsFunc(a.UsageStatFn)
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AnonSessionService) UsageStatFn(ctx context.Context) (map[string]interface{}, error) {
|
||||
sessionCount, err := a.remoteCache.Count(ctx, anonCachePrefix)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"stats.anonymous.session.count": sessionCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *AnonSessionService) TagSession(ctx context.Context, httpReq *http.Request) error {
|
||||
addr := web.RemoteAddr(httpReq)
|
||||
ip, err := network.GetIPFromAddress(addr)
|
||||
if err != nil {
|
||||
a.log.Debug("failed to parse ip from address", "addr", addr)
|
||||
return nil
|
||||
}
|
||||
|
||||
clientIPStr := ip.String()
|
||||
if len(ip) == 0 {
|
||||
clientIPStr = ""
|
||||
}
|
||||
|
||||
anonSession := &AnonSession{
|
||||
ip: clientIPStr,
|
||||
userAgent: httpReq.UserAgent(),
|
||||
}
|
||||
|
||||
key, err := anonSession.Key()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.remoteCache.Set(ctx, key, key, thirtyDays)
|
||||
}
|
13
pkg/services/anonymous/anontest/fake.go
Normal file
13
pkg/services/anonymous/anontest/fake.go
Normal file
@ -0,0 +1,13 @@
|
||||
package anontest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type FakeAnonymousSessionService struct {
|
||||
}
|
||||
|
||||
func (f *FakeAnonymousSessionService) TagSession(ctx context.Context, httpReq *http.Request) error {
|
||||
return nil
|
||||
}
|
10
pkg/services/anonymous/service.go
Normal file
10
pkg/services/anonymous/service.go
Normal file
@ -0,0 +1,10 @@
|
||||
package anonymous
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
TagSession(context.Context, *http.Request) error
|
||||
}
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/network"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
@ -16,6 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
@ -54,7 +54,7 @@ func ProvideService(
|
||||
apikeyService apikey.Service, userService user.Service,
|
||||
jwtService auth.JWTVerifierService,
|
||||
usageStats usagestats.Service,
|
||||
kvstore kvstore.KVStore,
|
||||
anonSessionService anonymous.Service,
|
||||
userProtectionService login.UserProtectionService,
|
||||
loginAttempts loginattempt.Service, quotaService quota.Service,
|
||||
authInfoService login.AuthInfoService, renderService rendering.Service,
|
||||
@ -83,7 +83,7 @@ func ProvideService(
|
||||
}
|
||||
|
||||
if s.cfg.AnonymousEnabled {
|
||||
s.RegisterClient(clients.ProvideAnonymous(cfg, orgService, kvstore))
|
||||
s.RegisterClient(clients.ProvideAnonymous(cfg, orgService, anonSessionService))
|
||||
}
|
||||
|
||||
var proxyClients []authn.ProxyClient
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@ -13,18 +13,20 @@ import (
|
||||
|
||||
var _ authn.ContextAwareClient = new(Anonymous)
|
||||
|
||||
func ProvideAnonymous(cfg *setting.Cfg, orgService org.Service, _ kvstore.KVStore) *Anonymous {
|
||||
func ProvideAnonymous(cfg *setting.Cfg, orgService org.Service, anonSessionService anonymous.Service) *Anonymous {
|
||||
return &Anonymous{
|
||||
cfg: cfg,
|
||||
log: log.New("authn.anonymous"),
|
||||
orgService: orgService,
|
||||
cfg: cfg,
|
||||
log: log.New("authn.anonymous"),
|
||||
orgService: orgService,
|
||||
anonSessionService: anonSessionService,
|
||||
}
|
||||
}
|
||||
|
||||
type Anonymous struct {
|
||||
cfg *setting.Cfg
|
||||
log log.Logger
|
||||
orgService org.Service
|
||||
cfg *setting.Cfg
|
||||
log log.Logger
|
||||
orgService org.Service
|
||||
anonSessionService anonymous.Service
|
||||
}
|
||||
|
||||
func (a *Anonymous) Name() string {
|
||||
@ -38,6 +40,17 @@ func (a *Anonymous) Authenticate(ctx context.Context, r *authn.Request) (*authn.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
a.log.Warn("tag anon session panic", "err", err)
|
||||
}
|
||||
}()
|
||||
if err := a.anonSessionService.TagSession(ctx, r.HTTPRequest); err != nil {
|
||||
a.log.Warn("Failed to tag anonymous session", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &authn.Identity{
|
||||
IsAnonymous: true,
|
||||
OrgID: o.ID,
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anontest"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||
@ -45,9 +46,10 @@ func TestAnonymous_Authenticate(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
c := Anonymous{
|
||||
cfg: tt.cfg,
|
||||
log: log.NewNopLogger(),
|
||||
orgService: &orgtest.FakeOrgService{ExpectedOrg: tt.org, ExpectedError: tt.err},
|
||||
cfg: tt.cfg,
|
||||
log: log.NewNopLogger(),
|
||||
orgService: &orgtest.FakeOrgService{ExpectedOrg: tt.org, ExpectedError: tt.err},
|
||||
anonSessionService: &anontest.FakeAnonymousSessionService{},
|
||||
}
|
||||
|
||||
identity, err := c.Authenticate(context.Background(), &authn.Request{})
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anontest"
|
||||
"github.com/grafana/grafana/pkg/services/auth/authtest"
|
||||
"github.com/grafana/grafana/pkg/services/auth/jwt"
|
||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||
@ -116,7 +117,8 @@ func getContextHandler(t *testing.T) *ContextHandler {
|
||||
|
||||
return ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc,
|
||||
renderSvc, sqlStore, tracer, authProxy, loginService, nil, authenticator,
|
||||
&userService, orgService, nil, featuremgmt.WithFeatures(), &authntest.FakeService{})
|
||||
&userService, orgService, nil, featuremgmt.WithFeatures(),
|
||||
&authntest.FakeService{}, &anontest.FakeAnonymousSessionService{})
|
||||
}
|
||||
|
||||
type fakeAuthenticator struct{}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
loginpkg "github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/middleware/cookies"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/auth/jwt"
|
||||
@ -53,48 +54,50 @@ func ProvideService(cfg *setting.Cfg, tokenService auth.UserTokenService, jwtSer
|
||||
tracer tracing.Tracer, authProxy *authproxy.AuthProxy, loginService login.Service,
|
||||
apiKeyService apikey.Service, authenticator loginpkg.Authenticator, userService user.Service,
|
||||
orgService org.Service, oauthTokenService oauthtoken.OAuthTokenService, features *featuremgmt.FeatureManager,
|
||||
authnService authn.Service,
|
||||
authnService authn.Service, anonSessionService anonymous.Service,
|
||||
) *ContextHandler {
|
||||
return &ContextHandler{
|
||||
Cfg: cfg,
|
||||
AuthTokenService: tokenService,
|
||||
JWTAuthService: jwtService,
|
||||
RemoteCache: remoteCache,
|
||||
RenderService: renderService,
|
||||
SQLStore: sqlStore,
|
||||
tracer: tracer,
|
||||
authProxy: authProxy,
|
||||
authenticator: authenticator,
|
||||
loginService: loginService,
|
||||
apiKeyService: apiKeyService,
|
||||
userService: userService,
|
||||
orgService: orgService,
|
||||
oauthTokenService: oauthTokenService,
|
||||
features: features,
|
||||
authnService: authnService,
|
||||
singleflight: new(singleflight.Group),
|
||||
Cfg: cfg,
|
||||
AuthTokenService: tokenService,
|
||||
JWTAuthService: jwtService,
|
||||
RemoteCache: remoteCache,
|
||||
RenderService: renderService,
|
||||
SQLStore: sqlStore,
|
||||
tracer: tracer,
|
||||
authProxy: authProxy,
|
||||
authenticator: authenticator,
|
||||
loginService: loginService,
|
||||
apiKeyService: apiKeyService,
|
||||
userService: userService,
|
||||
orgService: orgService,
|
||||
oauthTokenService: oauthTokenService,
|
||||
features: features,
|
||||
authnService: authnService,
|
||||
anonSessionService: anonSessionService,
|
||||
singleflight: new(singleflight.Group),
|
||||
}
|
||||
}
|
||||
|
||||
// ContextHandler is a middleware.
|
||||
type ContextHandler struct {
|
||||
Cfg *setting.Cfg
|
||||
AuthTokenService auth.UserTokenService
|
||||
JWTAuthService auth.JWTVerifierService
|
||||
RemoteCache *remotecache.RemoteCache
|
||||
RenderService rendering.Service
|
||||
SQLStore db.DB
|
||||
tracer tracing.Tracer
|
||||
authProxy *authproxy.AuthProxy
|
||||
authenticator loginpkg.Authenticator
|
||||
loginService login.Service
|
||||
apiKeyService apikey.Service
|
||||
userService user.Service
|
||||
orgService org.Service
|
||||
oauthTokenService oauthtoken.OAuthTokenService
|
||||
features *featuremgmt.FeatureManager
|
||||
authnService authn.Service
|
||||
singleflight *singleflight.Group
|
||||
Cfg *setting.Cfg
|
||||
AuthTokenService auth.UserTokenService
|
||||
JWTAuthService auth.JWTVerifierService
|
||||
RemoteCache *remotecache.RemoteCache
|
||||
RenderService rendering.Service
|
||||
SQLStore db.DB
|
||||
tracer tracing.Tracer
|
||||
authProxy *authproxy.AuthProxy
|
||||
authenticator loginpkg.Authenticator
|
||||
loginService login.Service
|
||||
apiKeyService apikey.Service
|
||||
userService user.Service
|
||||
orgService org.Service
|
||||
oauthTokenService oauthtoken.OAuthTokenService
|
||||
features *featuremgmt.FeatureManager
|
||||
authnService authn.Service
|
||||
singleflight *singleflight.Group
|
||||
anonSessionService anonymous.Service
|
||||
// GetTime returns the current time.
|
||||
// Stubbable by tests.
|
||||
GetTime func() time.Time
|
||||
@ -234,6 +237,17 @@ func (h *ContextHandler) initContextWithAnonymousUser(reqContext *contextmodel.R
|
||||
return false
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
reqContext.Logger.Warn("tag anon session panic", "err", err)
|
||||
}
|
||||
}()
|
||||
if err := h.anonSessionService.TagSession(reqContext.Req.Context(), reqContext.Req); err != nil {
|
||||
reqContext.Logger.Warn("Failed to tag anonymous session", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
reqContext.IsSignedIn = false
|
||||
reqContext.AllowAnonymous = true
|
||||
reqContext.SignedInUser = &user.SignedInUser{IsAnonymous: true}
|
||||
|
Loading…
Reference in New Issue
Block a user