extract auth token interface and remove auth token from context
This commit is contained in:
@@ -47,14 +47,14 @@ type HTTPServer struct {
|
||||
streamManager *live.StreamManager
|
||||
httpSrv *http.Server
|
||||
|
||||
RouteRegister routing.RouteRegister `inject:""`
|
||||
Bus bus.Bus `inject:""`
|
||||
RenderService rendering.Service `inject:""`
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
HooksService *hooks.HooksService `inject:""`
|
||||
CacheService *cache.CacheService `inject:""`
|
||||
DatasourceCache datasources.CacheService `inject:""`
|
||||
AuthTokenService *auth.UserAuthTokenService `inject:""`
|
||||
RouteRegister routing.RouteRegister `inject:""`
|
||||
Bus bus.Bus `inject:""`
|
||||
RenderService rendering.Service `inject:""`
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
HooksService *hooks.HooksService `inject:""`
|
||||
CacheService *cache.CacheService `inject:""`
|
||||
DatasourceCache datasources.CacheService `inject:""`
|
||||
AuthTokenService auth.UserAuthTokenService `inject:""`
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Init() error {
|
||||
|
||||
@@ -21,7 +21,7 @@ var (
|
||||
ReqOrgAdmin = RoleAuth(m.ROLE_ADMIN)
|
||||
)
|
||||
|
||||
func GetContextHandler(ats *auth.UserAuthTokenService) macaron.Handler {
|
||||
func GetContextHandler(ats auth.UserAuthTokenService) macaron.Handler {
|
||||
return func(c *macaron.Context) {
|
||||
ctx := &m.ReqContext{
|
||||
Context: c,
|
||||
|
||||
@@ -10,25 +10,9 @@ import (
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
type UserAuthToken struct {
|
||||
Id int64
|
||||
UserId int64
|
||||
AuthToken string
|
||||
PrevAuthToken string
|
||||
UserAgent string
|
||||
ClientIp string
|
||||
AuthTokenSeen bool
|
||||
SeenAt int64
|
||||
RotatedAt int64
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
UnhashedToken string `xorm:"-"`
|
||||
}
|
||||
|
||||
type ReqContext struct {
|
||||
*macaron.Context
|
||||
*SignedInUser
|
||||
UserToken *UserAuthToken
|
||||
|
||||
Session session.SessionStore
|
||||
|
||||
|
||||
@@ -18,67 +18,72 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.RegisterService(&UserAuthTokenService{})
|
||||
registry.RegisterService(&UserAuthTokenServiceImpl{})
|
||||
}
|
||||
|
||||
var (
|
||||
getTime = time.Now
|
||||
RotateTime = 30 * time.Second
|
||||
UrgentRotateTime = 10 * time.Second
|
||||
RotateTime = 2 * time.Minute
|
||||
UrgentRotateTime = 20 * time.Second
|
||||
oneYearInSeconds = 31557600 //used as default maxage for session cookies. We validate/rotate them more often.
|
||||
)
|
||||
|
||||
// UserAuthTokenService are used for generating and validating user auth tokens
|
||||
type UserAuthTokenService struct {
|
||||
type UserAuthTokenService interface {
|
||||
InitContextWithToken(ctx *models.ReqContext, orgID int64) bool
|
||||
UserAuthenticatedHook(user *models.User, c *models.ReqContext) error
|
||||
UserSignedOutHook(c *models.ReqContext)
|
||||
}
|
||||
|
||||
type UserAuthTokenServiceImpl struct {
|
||||
SQLStore *sqlstore.SqlStore `inject:""`
|
||||
ServerLockService *serverlock.ServerLockService `inject:""`
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// Init this service
|
||||
func (s *UserAuthTokenService) Init() error {
|
||||
func (s *UserAuthTokenServiceImpl) Init() error {
|
||||
s.log = log.New("auth")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *UserAuthTokenService) InitContextWithToken(ctx *models.ReqContext, orgID int64) bool {
|
||||
func (s *UserAuthTokenServiceImpl) InitContextWithToken(ctx *models.ReqContext, orgID int64) bool {
|
||||
//auth User
|
||||
unhashedToken := ctx.GetCookie(setting.SessionOptions.CookieName)
|
||||
if unhashedToken == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
user, err := s.LookupToken(unhashedToken)
|
||||
userToken, err := s.LookupToken(unhashedToken)
|
||||
if err != nil {
|
||||
ctx.Logger.Info("failed to look up user based on cookie", "error", err)
|
||||
return false
|
||||
}
|
||||
|
||||
query := models.GetSignedInUserQuery{UserId: user.UserId, OrgId: orgID}
|
||||
query := models.GetSignedInUserQuery{UserId: userToken.UserId, OrgId: orgID}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
ctx.Logger.Error("Failed to get user with id", "userId", user.UserId, "error", err)
|
||||
ctx.Logger.Error("Failed to get user with id", "userId", userToken.UserId, "error", err)
|
||||
return false
|
||||
}
|
||||
|
||||
ctx.SignedInUser = query.Result
|
||||
ctx.IsSignedIn = true
|
||||
ctx.UserToken = user
|
||||
|
||||
//rotate session token if needed.
|
||||
rotated, err := s.RefreshToken(ctx.UserToken, ctx.RemoteAddr(), ctx.Req.UserAgent())
|
||||
rotated, err := s.RefreshToken(userToken, ctx.RemoteAddr(), ctx.Req.UserAgent())
|
||||
if err != nil {
|
||||
ctx.Logger.Error("failed to rotate token", "error", err, "user.id", user.UserId, "user_token.id", user.Id)
|
||||
ctx.Logger.Error("failed to rotate token", "error", err, "userId", userToken.UserId, "tokenId", userToken.Id)
|
||||
return true
|
||||
}
|
||||
|
||||
if rotated {
|
||||
s.writeSessionCookie(ctx, ctx.UserToken.UnhashedToken, oneYearInSeconds)
|
||||
s.writeSessionCookie(ctx, userToken.UnhashedToken, oneYearInSeconds)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *UserAuthTokenService) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) {
|
||||
func (s *UserAuthTokenServiceImpl) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) {
|
||||
ctx.Logger.Info("new token", "unhashed token", value)
|
||||
|
||||
ctx.Resp.Header().Del("Set-Cookie")
|
||||
@@ -94,23 +99,21 @@ func (s *UserAuthTokenService) writeSessionCookie(ctx *models.ReqContext, value
|
||||
http.SetCookie(ctx.Resp, &cookie)
|
||||
}
|
||||
|
||||
func (s *UserAuthTokenService) UserAuthenticatedHook(user *models.User, c *models.ReqContext) error {
|
||||
func (s *UserAuthTokenServiceImpl) UserAuthenticatedHook(user *models.User, c *models.ReqContext) error {
|
||||
userToken, err := s.CreateToken(user.Id, c.RemoteAddr(), c.Req.UserAgent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.UserToken = userToken
|
||||
|
||||
s.writeSessionCookie(c, userToken.UnhashedToken, oneYearInSeconds)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *UserAuthTokenService) UserSignedOutHook(c *models.ReqContext) {
|
||||
func (s *UserAuthTokenServiceImpl) UserSignedOutHook(c *models.ReqContext) {
|
||||
s.writeSessionCookie(c, "", -1)
|
||||
}
|
||||
|
||||
func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (*models.UserAuthToken, error) {
|
||||
func (s *UserAuthTokenServiceImpl) CreateToken(userId int64, clientIP, userAgent string) (*userAuthToken, error) {
|
||||
clientIP = util.ParseIPAddress(clientIP)
|
||||
token, err := util.RandomHex(16)
|
||||
if err != nil {
|
||||
@@ -121,7 +124,7 @@ func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent str
|
||||
|
||||
now := getTime().Unix()
|
||||
|
||||
userToken := models.UserAuthToken{
|
||||
userToken := userAuthToken{
|
||||
UserId: userId,
|
||||
AuthToken: hashedToken,
|
||||
PrevAuthToken: hashedToken,
|
||||
@@ -143,11 +146,11 @@ func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent str
|
||||
return &userToken, nil
|
||||
}
|
||||
|
||||
func (s *UserAuthTokenService) LookupToken(unhashedToken string) (*models.UserAuthToken, error) {
|
||||
func (s *UserAuthTokenServiceImpl) LookupToken(unhashedToken string) (*userAuthToken, error) {
|
||||
hashedToken := hashToken(unhashedToken)
|
||||
expireBefore := getTime().Add(time.Duration(-86400*setting.LogInRememberDays) * time.Second).Unix()
|
||||
|
||||
var userToken models.UserAuthToken
|
||||
var userToken userAuthToken
|
||||
exists, err := s.SQLStore.NewSession().Where("(auth_token = ? OR prev_auth_token = ?) AND created_at > ?", hashedToken, hashedToken, expireBefore).Get(&userToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -198,7 +201,7 @@ func (s *UserAuthTokenService) LookupToken(unhashedToken string) (*models.UserAu
|
||||
return &userToken, nil
|
||||
}
|
||||
|
||||
func (s *UserAuthTokenService) RefreshToken(token *models.UserAuthToken, clientIP, userAgent string) (bool, error) {
|
||||
func (s *UserAuthTokenServiceImpl) RefreshToken(token *userAuthToken, clientIP, userAgent string) (bool, error) {
|
||||
if token == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
@@ -290,7 +289,7 @@ func createTestContext(t *testing.T) *testContext {
|
||||
t.Helper()
|
||||
|
||||
sqlstore := sqlstore.InitTestDB(t)
|
||||
tokenService := &UserAuthTokenService{
|
||||
tokenService := &UserAuthTokenServiceImpl{
|
||||
SQLStore: sqlstore,
|
||||
log: log.New("test-logger"),
|
||||
}
|
||||
@@ -307,12 +306,12 @@ func createTestContext(t *testing.T) *testContext {
|
||||
|
||||
type testContext struct {
|
||||
sqlstore *sqlstore.SqlStore
|
||||
tokenService *UserAuthTokenService
|
||||
tokenService *UserAuthTokenServiceImpl
|
||||
}
|
||||
|
||||
func (c *testContext) getAuthTokenByID(id int64) (*models.UserAuthToken, error) {
|
||||
func (c *testContext) getAuthTokenByID(id int64) (*userAuthToken, error) {
|
||||
sess := c.sqlstore.NewSession()
|
||||
var t models.UserAuthToken
|
||||
var t userAuthToken
|
||||
found, err := sess.ID(id).Get(&t)
|
||||
if err != nil || !found {
|
||||
return nil, err
|
||||
|
||||
+14
-14
@@ -9,17 +9,17 @@ var (
|
||||
ErrAuthTokenNotFound = errors.New("User auth token not found")
|
||||
)
|
||||
|
||||
// type userAuthToken struct {
|
||||
// Id int64
|
||||
// UserId int64
|
||||
// AuthToken string
|
||||
// PrevAuthToken string
|
||||
// UserAgent string
|
||||
// ClientIp string
|
||||
// AuthTokenSeen bool
|
||||
// SeenAt int64
|
||||
// RotatedAt int64
|
||||
// CreatedAt int64
|
||||
// UpdatedAt int64
|
||||
// unhashedToken string `xorm:"-"`
|
||||
// }
|
||||
type userAuthToken struct {
|
||||
Id int64
|
||||
UserId int64
|
||||
AuthToken string
|
||||
PrevAuthToken string
|
||||
UserAgent string
|
||||
ClientIp string
|
||||
AuthTokenSeen bool
|
||||
SeenAt int64
|
||||
RotatedAt int64
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
UnhashedToken string `xorm:"-"`
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func (srv *UserAuthTokenService) Run(ctx context.Context) error {
|
||||
func (srv *UserAuthTokenServiceImpl) Run(ctx context.Context) error {
|
||||
ticker := time.NewTicker(time.Hour * 12)
|
||||
deleteSessionAfter := time.Hour * 24 * 7 * 30
|
||||
|
||||
@@ -22,7 +22,7 @@ func (srv *UserAuthTokenService) Run(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *UserAuthTokenService) deleteOldSession(deleteSessionAfter time.Duration) (int64, error) {
|
||||
func (srv *UserAuthTokenServiceImpl) deleteOldSession(deleteSessionAfter time.Duration) (int64, error) {
|
||||
sql := `DELETE from user_auth_token WHERE rotated_at < ?`
|
||||
|
||||
deleteBefore := getTime().Add(-deleteSessionAfter)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
@@ -15,7 +14,7 @@ func TestUserAuthTokenCleanup(t *testing.T) {
|
||||
ctx := createTestContext(t)
|
||||
|
||||
insertToken := func(token string, prev string, rotatedAt int64) {
|
||||
ut := models.UserAuthToken{AuthToken: token, PrevAuthToken: prev, RotatedAt: rotatedAt, UserAgent: "", ClientIp: ""}
|
||||
ut := userAuthToken{AuthToken: token, PrevAuthToken: prev, RotatedAt: rotatedAt, UserAgent: "", ClientIp: ""}
|
||||
_, err := ctx.sqlstore.NewSession().Insert(&ut)
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user