184 lines
4.6 KiB
Go
184 lines
4.6 KiB
Go
package authorization
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
|
"github.com/influxdata/influxdb/v2/mock"
|
|
"github.com/influxdata/influxdb/v2/tenant"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestCachingPasswordsService(t *testing.T) {
|
|
const (
|
|
user1 = platform.ID(1)
|
|
user2 = platform.ID(2)
|
|
)
|
|
|
|
makeUser := func(salt, pass string) authUser {
|
|
if len(salt) != SaltBytes {
|
|
panic("invalid salt")
|
|
}
|
|
|
|
var ps CachingPasswordsService
|
|
return authUser{salt: []byte(salt), hash: ps.hashWithSalt([]byte(salt), pass)}
|
|
}
|
|
|
|
var (
|
|
userE1 = makeUser(strings.Repeat("salt---1", 4), "foo")
|
|
userE2 = makeUser(strings.Repeat("salt---2", 4), "bar")
|
|
)
|
|
|
|
t.Run("SetPassword deletes cached user", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
inner := mock.NewMockPasswordsService(ctrl)
|
|
inner.EXPECT().
|
|
SetPassword(gomock.Any(), user1, "foo").
|
|
Return(nil)
|
|
|
|
s := NewCachingPasswordsService(inner)
|
|
s.authCache[user1] = userE1
|
|
s.authCache[user2] = userE2
|
|
|
|
ctx := context.Background()
|
|
|
|
_, ok := s.authCache[user1]
|
|
assert.True(t, ok)
|
|
assert.NoError(t, s.SetPassword(ctx, user1, "foo"))
|
|
_, ok = s.authCache[user1]
|
|
assert.False(t, ok)
|
|
_, ok = s.authCache[user2]
|
|
assert.True(t, ok)
|
|
})
|
|
|
|
t.Run("ComparePassword adds cached user", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
inner := mock.NewMockPasswordsService(ctrl)
|
|
inner.EXPECT().
|
|
ComparePassword(gomock.Any(), user1, "foo").
|
|
Return(nil)
|
|
|
|
s := NewCachingPasswordsService(inner)
|
|
s.authCache[user2] = userE2
|
|
|
|
ctx := context.Background()
|
|
|
|
assert.NoError(t, s.ComparePassword(ctx, user1, "foo"))
|
|
_, ok := s.authCache[user1]
|
|
assert.True(t, ok)
|
|
_, ok = s.authCache[user2]
|
|
assert.True(t, ok)
|
|
})
|
|
|
|
t.Run("ComparePassword does not add cached user when inner errors", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
inner := mock.NewMockPasswordsService(ctrl)
|
|
inner.EXPECT().
|
|
ComparePassword(gomock.Any(), user1, "foo").
|
|
Return(tenant.EShortPassword)
|
|
|
|
s := NewCachingPasswordsService(inner)
|
|
s.authCache[user2] = userE2
|
|
|
|
ctx := context.Background()
|
|
|
|
assert.Error(t, s.ComparePassword(ctx, user1, "foo"))
|
|
_, ok := s.authCache[user1]
|
|
assert.False(t, ok)
|
|
_, ok = s.authCache[user2]
|
|
assert.True(t, ok)
|
|
})
|
|
|
|
t.Run("ComparePassword uses cached password when context option set", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
inner := mock.NewMockPasswordsService(ctrl)
|
|
s := NewCachingPasswordsService(inner)
|
|
s.authCache[user1] = userE1
|
|
s.authCache[user2] = userE2
|
|
|
|
ctx := context.Background()
|
|
assert.NoError(t, s.ComparePassword(ctx, user1, "foo"))
|
|
})
|
|
|
|
t.Run("CompareAndSetPassword deletes cached user", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
inner := mock.NewMockPasswordsService(ctrl)
|
|
inner.EXPECT().
|
|
CompareAndSetPassword(gomock.Any(), user1, "foo", "foo2").
|
|
Return(nil)
|
|
|
|
s := NewCachingPasswordsService(inner)
|
|
s.authCache[user1] = userE1
|
|
s.authCache[user2] = userE2
|
|
|
|
ctx := context.Background()
|
|
|
|
assert.NoError(t, s.CompareAndSetPassword(ctx, user1, "foo", "foo2"))
|
|
_, ok := s.authCache[user1]
|
|
assert.False(t, ok)
|
|
_, ok = s.authCache[user2]
|
|
assert.True(t, ok)
|
|
})
|
|
|
|
// The following tests ensure the service does not change state for invalid
|
|
// requests, which may permit a certain class of attacks.
|
|
|
|
t.Run("SetPassword does not delete cached user when inner errors", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
inner := mock.NewMockPasswordsService(ctrl)
|
|
inner.EXPECT().
|
|
SetPassword(gomock.Any(), user1, "foo").
|
|
Return(tenant.EShortPassword)
|
|
|
|
s := NewCachingPasswordsService(inner)
|
|
s.authCache[user1] = userE1
|
|
s.authCache[user2] = userE2
|
|
|
|
ctx := context.Background()
|
|
|
|
_, ok := s.authCache[user1]
|
|
assert.True(t, ok)
|
|
assert.EqualError(t, s.SetPassword(ctx, user1, "foo"), tenant.EShortPassword.Error())
|
|
_, ok = s.authCache[user1]
|
|
assert.True(t, ok)
|
|
})
|
|
|
|
t.Run("CompareAndSetPassword does not delete cached user when inner errors", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
inner := mock.NewMockPasswordsService(ctrl)
|
|
inner.EXPECT().
|
|
CompareAndSetPassword(gomock.Any(), user1, "foo", "foo2").
|
|
Return(tenant.EShortPassword)
|
|
|
|
s := NewCachingPasswordsService(inner)
|
|
s.authCache[user1] = userE1
|
|
s.authCache[user2] = userE2
|
|
|
|
ctx := context.Background()
|
|
|
|
assert.Error(t, s.CompareAndSetPassword(ctx, user1, "foo", "foo2"))
|
|
_, ok := s.authCache[user1]
|
|
assert.True(t, ok)
|
|
_, ok = s.authCache[user2]
|
|
assert.True(t, ok)
|
|
})
|
|
|
|
}
|