mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 20:24:18 -06:00
340 lines
9.9 KiB
Go
340 lines
9.9 KiB
Go
package auth
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
|
|
"github.com/grafana/grafana/pkg/log"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
)
|
|
|
|
func TestUserAuthToken(t *testing.T) {
|
|
Convey("Test user auth token", t, func() {
|
|
ctx := createTestContext(t)
|
|
userAuthTokenService := ctx.tokenService
|
|
userID := int64(10)
|
|
|
|
t := time.Date(2018, 12, 13, 13, 45, 0, 0, time.UTC)
|
|
getTime = func() time.Time {
|
|
return t
|
|
}
|
|
|
|
Convey("When creating token", func() {
|
|
token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent")
|
|
So(err, ShouldBeNil)
|
|
So(token, ShouldNotBeNil)
|
|
So(token.AuthTokenSeen, ShouldBeFalse)
|
|
|
|
Convey("When lookup unhashed token should return user auth token", func() {
|
|
LookupToken, err := userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
So(LookupToken, ShouldNotBeNil)
|
|
So(LookupToken.UserId, ShouldEqual, userID)
|
|
So(LookupToken.AuthTokenSeen, ShouldBeTrue)
|
|
|
|
storedAuthToken, err := ctx.getAuthTokenByID(LookupToken.Id)
|
|
So(err, ShouldBeNil)
|
|
So(storedAuthToken, ShouldNotBeNil)
|
|
So(storedAuthToken.AuthTokenSeen, ShouldBeTrue)
|
|
})
|
|
|
|
Convey("When lookup hashed token should return user auth token not found error", func() {
|
|
LookupToken, err := userAuthTokenService.LookupToken(token.AuthToken)
|
|
So(err, ShouldEqual, ErrAuthTokenNotFound)
|
|
So(LookupToken, ShouldBeNil)
|
|
})
|
|
})
|
|
|
|
Convey("expires correctly", func() {
|
|
token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent")
|
|
So(err, ShouldBeNil)
|
|
So(token, ShouldNotBeNil)
|
|
|
|
_, err = userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
|
|
token, err = ctx.getAuthTokenByID(token.Id)
|
|
So(err, ShouldBeNil)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(time.Hour)
|
|
}
|
|
|
|
refreshed, err := userAuthTokenService.RefreshToken(token, "192.168.10.11:1234", "some user agent")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeTrue)
|
|
|
|
_, err = userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
|
|
stillGood, err := userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
So(stillGood, ShouldNotBeNil)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(24 * 7 * time.Hour)
|
|
}
|
|
notGood, err := userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldEqual, ErrAuthTokenNotFound)
|
|
So(notGood, ShouldBeNil)
|
|
})
|
|
|
|
Convey("can properly rotate tokens", func() {
|
|
token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent")
|
|
So(err, ShouldBeNil)
|
|
So(token, ShouldNotBeNil)
|
|
|
|
prevToken := token.AuthToken
|
|
unhashedPrev := token.UnhashedToken
|
|
|
|
refreshed, err := userAuthTokenService.RefreshToken(token, "192.168.10.12:1234", "a new user agent")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeFalse)
|
|
|
|
updated, err := ctx.markAuthTokenAsSeen(token.Id)
|
|
So(err, ShouldBeNil)
|
|
So(updated, ShouldBeTrue)
|
|
|
|
token, err = ctx.getAuthTokenByID(token.Id)
|
|
So(err, ShouldBeNil)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(time.Hour)
|
|
}
|
|
|
|
refreshed, err = userAuthTokenService.RefreshToken(token, "192.168.10.12:1234", "a new user agent")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeTrue)
|
|
|
|
unhashedToken := token.UnhashedToken
|
|
|
|
token, err = ctx.getAuthTokenByID(token.Id)
|
|
So(err, ShouldBeNil)
|
|
token.UnhashedToken = unhashedToken
|
|
|
|
So(token.RotatedAt, ShouldEqual, getTime().Unix())
|
|
So(token.ClientIp, ShouldEqual, "192.168.10.12")
|
|
So(token.UserAgent, ShouldEqual, "a new user agent")
|
|
So(token.AuthTokenSeen, ShouldBeFalse)
|
|
So(token.SeenAt, ShouldEqual, 0)
|
|
So(token.PrevAuthToken, ShouldEqual, prevToken)
|
|
|
|
// ability to auth using an old token
|
|
|
|
lookedUp, err := userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
So(lookedUp.AuthTokenSeen, ShouldBeTrue)
|
|
So(lookedUp.SeenAt, ShouldEqual, getTime().Unix())
|
|
|
|
lookedUp, err = userAuthTokenService.LookupToken(unhashedPrev)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
So(lookedUp.Id, ShouldEqual, token.Id)
|
|
So(lookedUp.AuthTokenSeen, ShouldBeTrue)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(time.Hour + (2 * time.Minute))
|
|
}
|
|
|
|
lookedUp, err = userAuthTokenService.LookupToken(unhashedPrev)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
So(lookedUp.AuthTokenSeen, ShouldBeTrue)
|
|
|
|
lookedUp, err = ctx.getAuthTokenByID(lookedUp.Id)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
So(lookedUp.AuthTokenSeen, ShouldBeFalse)
|
|
|
|
refreshed, err = userAuthTokenService.RefreshToken(token, "192.168.10.12:1234", "a new user agent")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeTrue)
|
|
|
|
token, err = ctx.getAuthTokenByID(token.Id)
|
|
So(err, ShouldBeNil)
|
|
So(token, ShouldNotBeNil)
|
|
So(token.SeenAt, ShouldEqual, 0)
|
|
})
|
|
|
|
Convey("keeps prev token valid for 1 minute after it is confirmed", func() {
|
|
token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent")
|
|
So(err, ShouldBeNil)
|
|
So(token, ShouldNotBeNil)
|
|
|
|
lookedUp, err := userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(10 * time.Minute)
|
|
}
|
|
|
|
prevToken := token.UnhashedToken
|
|
refreshed, err := userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeTrue)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(20 * time.Minute)
|
|
}
|
|
|
|
current, err := userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
So(current, ShouldNotBeNil)
|
|
|
|
prev, err := userAuthTokenService.LookupToken(prevToken)
|
|
So(err, ShouldBeNil)
|
|
So(prev, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("will not mark token unseen when prev and current are the same", func() {
|
|
token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent")
|
|
So(err, ShouldBeNil)
|
|
So(token, ShouldNotBeNil)
|
|
|
|
lookedUp, err := userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
|
|
lookedUp, err = userAuthTokenService.LookupToken(token.UnhashedToken)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
|
|
lookedUp, err = ctx.getAuthTokenByID(lookedUp.Id)
|
|
So(err, ShouldBeNil)
|
|
So(lookedUp, ShouldNotBeNil)
|
|
So(lookedUp.AuthTokenSeen, ShouldBeTrue)
|
|
})
|
|
|
|
Convey("Rotate token", func() {
|
|
token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent")
|
|
So(err, ShouldBeNil)
|
|
So(token, ShouldNotBeNil)
|
|
|
|
prevToken := token.AuthToken
|
|
|
|
Convey("Should rotate current token and previous token when auth token seen", func() {
|
|
updated, err := ctx.markAuthTokenAsSeen(token.Id)
|
|
So(err, ShouldBeNil)
|
|
So(updated, ShouldBeTrue)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(10 * time.Minute)
|
|
}
|
|
|
|
refreshed, err := userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeTrue)
|
|
|
|
storedToken, err := ctx.getAuthTokenByID(token.Id)
|
|
So(err, ShouldBeNil)
|
|
So(storedToken, ShouldNotBeNil)
|
|
So(storedToken.AuthTokenSeen, ShouldBeFalse)
|
|
So(storedToken.PrevAuthToken, ShouldEqual, prevToken)
|
|
So(storedToken.AuthToken, ShouldNotEqual, prevToken)
|
|
|
|
prevToken = storedToken.AuthToken
|
|
|
|
updated, err = ctx.markAuthTokenAsSeen(token.Id)
|
|
So(err, ShouldBeNil)
|
|
So(updated, ShouldBeTrue)
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(20 * time.Minute)
|
|
}
|
|
|
|
refreshed, err = userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeTrue)
|
|
|
|
storedToken, err = ctx.getAuthTokenByID(token.Id)
|
|
So(err, ShouldBeNil)
|
|
So(storedToken, ShouldNotBeNil)
|
|
So(storedToken.AuthTokenSeen, ShouldBeFalse)
|
|
So(storedToken.PrevAuthToken, ShouldEqual, prevToken)
|
|
So(storedToken.AuthToken, ShouldNotEqual, prevToken)
|
|
})
|
|
|
|
Convey("Should rotate current token, but keep previous token when auth token not seen", func() {
|
|
token.RotatedAt = getTime().Add(-2 * time.Minute).Unix()
|
|
|
|
getTime = func() time.Time {
|
|
return t.Add(2 * time.Minute)
|
|
}
|
|
|
|
refreshed, err := userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox")
|
|
So(err, ShouldBeNil)
|
|
So(refreshed, ShouldBeTrue)
|
|
|
|
storedToken, err := ctx.getAuthTokenByID(token.Id)
|
|
So(err, ShouldBeNil)
|
|
So(storedToken, ShouldNotBeNil)
|
|
So(storedToken.AuthTokenSeen, ShouldBeFalse)
|
|
So(storedToken.PrevAuthToken, ShouldEqual, prevToken)
|
|
So(storedToken.AuthToken, ShouldNotEqual, prevToken)
|
|
})
|
|
})
|
|
|
|
Reset(func() {
|
|
getTime = time.Now
|
|
})
|
|
})
|
|
}
|
|
|
|
func createTestContext(t *testing.T) *testContext {
|
|
t.Helper()
|
|
|
|
sqlstore := sqlstore.InitTestDB(t)
|
|
tokenService := &UserAuthTokenServiceImpl{
|
|
SQLStore: sqlstore,
|
|
Cfg: &setting.Cfg{
|
|
LoginCookieName: "grafana_session",
|
|
LoginCookieMaxDays: 7,
|
|
LoginDeleteExpiredTokensAfterDays: 30,
|
|
LoginCookieRotation: 10,
|
|
},
|
|
log: log.New("test-logger"),
|
|
}
|
|
|
|
UrgentRotateTime = time.Minute
|
|
|
|
return &testContext{
|
|
sqlstore: sqlstore,
|
|
tokenService: tokenService,
|
|
}
|
|
}
|
|
|
|
type testContext struct {
|
|
sqlstore *sqlstore.SqlStore
|
|
tokenService *UserAuthTokenServiceImpl
|
|
}
|
|
|
|
func (c *testContext) getAuthTokenByID(id int64) (*userAuthToken, error) {
|
|
sess := c.sqlstore.NewSession()
|
|
var t userAuthToken
|
|
found, err := sess.ID(id).Get(&t)
|
|
if err != nil || !found {
|
|
return nil, err
|
|
}
|
|
|
|
return &t, nil
|
|
}
|
|
|
|
func (c *testContext) markAuthTokenAsSeen(id int64) (bool, error) {
|
|
sess := c.sqlstore.NewSession()
|
|
res, err := sess.Exec("UPDATE user_auth_token SET auth_token_seen = ? WHERE id = ?", c.sqlstore.Dialect.BooleanStr(true), id)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
rowsAffected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return rowsAffected == 1, nil
|
|
}
|