AuthToken: Remove client token rotation feature toggle (#82886)

* Remove usage of client token rotation flag

* Remove client token rotation feature toggle
This commit is contained in:
Karl Persson
2024-02-16 15:03:37 +01:00
committed by GitHub
parent 248031d007
commit 9e04fd0fb7
17 changed files with 36 additions and 278 deletions

View File

@@ -2,27 +2,20 @@ package clients
import (
"context"
"errors"
"net/url"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/network"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
)
var _ authn.HookClient = new(Session)
var _ authn.ContextAwareClient = new(Session)
func ProvideSession(cfg *setting.Cfg, sessionService auth.UserTokenService,
features featuremgmt.FeatureToggles) *Session {
func ProvideSession(cfg *setting.Cfg, sessionService auth.UserTokenService) *Session {
return &Session{
cfg: cfg,
features: features,
sessionService: sessionService,
log: log.New(authn.ClientSession),
}
@@ -30,7 +23,6 @@ func ProvideSession(cfg *setting.Cfg, sessionService auth.UserTokenService,
type Session struct {
cfg *setting.Cfg
features featuremgmt.FeatureToggles
sessionService auth.UserTokenService
log log.Logger
}
@@ -55,10 +47,8 @@ func (s *Session) Authenticate(ctx context.Context, r *authn.Request) (*authn.Id
return nil, err
}
if s.features.IsEnabled(ctx, featuremgmt.FlagClientTokenRotation) {
if token.NeedsRotation(time.Duration(s.cfg.TokenRotationIntervalMinutes) * time.Minute) {
return nil, authn.ErrTokenNeedsRotation.Errorf("token needs to be rotated")
}
if token.NeedsRotation(time.Duration(s.cfg.TokenRotationIntervalMinutes) * time.Minute) {
return nil, authn.ErrTokenNeedsRotation.Errorf("token needs to be rotated")
}
return &authn.Identity{
@@ -86,40 +76,3 @@ func (s *Session) Test(ctx context.Context, r *authn.Request) bool {
func (s *Session) Priority() uint {
return 60
}
func (s *Session) Hook(ctx context.Context, identity *authn.Identity, r *authn.Request) error {
if identity.SessionToken == nil || s.features.IsEnabled(ctx, featuremgmt.FlagClientTokenRotation) {
return nil
}
r.Resp.Before(func(w web.ResponseWriter) {
if w.Written() || errors.Is(ctx.Err(), context.Canceled) {
return
}
// FIXME (jguer): get real values
addr := web.RemoteAddr(r.HTTPRequest)
userAgent := r.HTTPRequest.UserAgent()
// addr := reqContext.RemoteAddr()
ip, err := network.GetIPFromAddress(addr)
if err != nil {
s.log.Debug("Failed to get client IP address", "addr", addr, "err", err)
ip = nil
}
rotated, newToken, err := s.sessionService.TryRotateToken(ctx, identity.SessionToken, ip, userAgent)
if err != nil {
s.log.Error("Failed to rotate token", "error", err)
return
}
if rotated {
identity.SessionToken = newToken
s.log.Debug("Rotated session token", "user", identity.ID)
authn.WriteSessionCookie(w, s.cfg, identity.SessionToken)
}
})
return nil
}

View File

@@ -2,7 +2,6 @@ package clients
import (
"context"
"net"
"net/http"
"testing"
"time"
@@ -14,9 +13,7 @@ import (
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/authtest"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
)
func TestSession_Test(t *testing.T) {
@@ -29,7 +26,7 @@ func TestSession_Test(t *testing.T) {
cfg := setting.NewCfg()
cfg.LoginCookieName = ""
cfg.LoginMaxLifetime = 20 * time.Second
s := ProvideSession(cfg, &authtest.FakeUserAuthTokenService{}, featuremgmt.WithFeatures())
s := ProvideSession(cfg, &authtest.FakeUserAuthTokenService{})
disabled := s.Test(context.Background(), &authn.Request{HTTPRequest: validHTTPReq})
assert.False(t, disabled)
@@ -64,7 +61,6 @@ func TestSession_Authenticate(t *testing.T) {
type fields struct {
sessionService auth.UserTokenService
features featuremgmt.FeatureToggles
}
type args struct {
r *authn.Request
@@ -80,7 +76,6 @@ func TestSession_Authenticate(t *testing.T) {
name: "cookie not found",
fields: fields{
sessionService: &authtest.FakeUserAuthTokenService{},
features: featuremgmt.WithFeatures(),
},
args: args{r: &authn.Request{HTTPRequest: &http.Request{}}},
wantID: nil,
@@ -92,7 +87,6 @@ func TestSession_Authenticate(t *testing.T) {
sessionService: &authtest.FakeUserAuthTokenService{LookupTokenProvider: func(ctx context.Context, unhashedToken string) (*auth.UserToken, error) {
return validToken, nil
}},
features: featuremgmt.WithFeatures(),
},
args: args{r: &authn.Request{HTTPRequest: validHTTPReq}},
wantID: &authn.Identity{
@@ -106,7 +100,7 @@ func TestSession_Authenticate(t *testing.T) {
wantErr: false,
},
{
name: "should return error for token that needs rotation if ClientTokenRotation is enabled",
name: "should return error for token that needs rotation",
fields: fields{
sessionService: &authtest.FakeUserAuthTokenService{LookupTokenProvider: func(ctx context.Context, unhashedToken string) (*auth.UserToken, error) {
return &auth.UserToken{
@@ -114,18 +108,16 @@ func TestSession_Authenticate(t *testing.T) {
RotatedAt: time.Now().Add(-11 * time.Minute).Unix(),
}, nil
}},
features: featuremgmt.WithFeatures(featuremgmt.FlagClientTokenRotation),
},
args: args{r: &authn.Request{HTTPRequest: validHTTPReq}},
wantErr: true,
},
{
name: "should return identity for token that don't need rotation if ClientTokenRotation is enabled",
name: "should return identity for token that don't need rotation",
fields: fields{
sessionService: &authtest.FakeUserAuthTokenService{LookupTokenProvider: func(ctx context.Context, unhashedToken string) (*auth.UserToken, error) {
return validToken, nil
}},
features: featuremgmt.WithFeatures(featuremgmt.FlagClientTokenRotation),
},
args: args{r: &authn.Request{HTTPRequest: validHTTPReq}},
wantID: &authn.Identity{
@@ -145,7 +137,7 @@ func TestSession_Authenticate(t *testing.T) {
cfg.LoginCookieName = cookieName
cfg.TokenRotationIntervalMinutes = 10
cfg.LoginMaxLifetime = 20 * time.Second
s := ProvideSession(cfg, tt.fields.sessionService, tt.fields.features)
s := ProvideSession(cfg, tt.fields.sessionService)
got, err := s.Authenticate(context.Background(), tt.args.r)
require.True(t, (err != nil) == tt.wantErr, err)
@@ -157,73 +149,3 @@ func TestSession_Authenticate(t *testing.T) {
})
}
}
type fakeResponseWriter struct {
Status int
HeaderStore http.Header
}
func (f *fakeResponseWriter) Header() http.Header {
return f.HeaderStore
}
func (f *fakeResponseWriter) Write([]byte) (int, error) {
return 0, nil
}
func (f *fakeResponseWriter) WriteHeader(statusCode int) {
f.Status = statusCode
}
func TestSession_Hook(t *testing.T) {
t.Run("should rotate token", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LoginCookieName = "grafana-session"
cfg.LoginMaxLifetime = 20 * time.Second
s := ProvideSession(cfg, &authtest.FakeUserAuthTokenService{
TryRotateTokenProvider: func(ctx context.Context, token *auth.UserToken, clientIP net.IP, userAgent string) (bool, *auth.UserToken, error) {
token.UnhashedToken = "new-token"
return true, token, nil
},
}, featuremgmt.WithFeatures())
sampleID := &authn.Identity{
SessionToken: &auth.UserToken{
Id: 1,
UserId: 1,
},
}
mockResponseWriter := &fakeResponseWriter{
Status: 0,
HeaderStore: map[string][]string{},
}
resp := &authn.Request{
HTTPRequest: &http.Request{
Header: map[string][]string{},
},
Resp: web.NewResponseWriter(http.MethodConnect, mockResponseWriter),
}
err := s.Hook(context.Background(), sampleID, resp)
require.NoError(t, err)
resp.Resp.WriteHeader(201)
require.Equal(t, 201, mockResponseWriter.Status)
assert.Equal(t, "new-token", sampleID.SessionToken.UnhashedToken)
require.Len(t, mockResponseWriter.HeaderStore, 1)
assert.Equal(t, "grafana-session=new-token; Path=/; Max-Age=20; HttpOnly",
mockResponseWriter.HeaderStore.Get("set-cookie"), mockResponseWriter.HeaderStore)
})
t.Run("should not rotate token with feature flag", func(t *testing.T) {
s := ProvideSession(setting.NewCfg(), nil, featuremgmt.WithFeatures(featuremgmt.FlagClientTokenRotation))
req := &authn.Request{}
identity := &authn.Identity{}
err := s.Hook(context.Background(), identity, req)
require.NoError(t, err)
})
}