2019-12-07 18:18:22 +07:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/golang/mock/gomock"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/suite"
|
2020-05-01 13:56:42 +07:00
|
|
|
|
|
|
|
"github.com/authelia/authelia/internal/mocks"
|
|
|
|
"github.com/authelia/authelia/internal/storage"
|
2019-12-07 18:18:22 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
type FetchSuite struct {
|
|
|
|
suite.Suite
|
|
|
|
mock *mocks.MockAutheliaCtx
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FetchSuite) SetupTest() {
|
|
|
|
s.mock = mocks.NewMockAutheliaCtx(s.T())
|
2020-01-06 06:03:16 +07:00
|
|
|
// Set the initial user session.
|
2019-12-07 18:18:22 +07:00
|
|
|
userSession := s.mock.Ctx.GetSession()
|
2020-05-02 23:20:40 +07:00
|
|
|
userSession.Username = testUsername
|
2019-12-07 18:18:22 +07:00
|
|
|
userSession.AuthenticationLevel = 1
|
2020-04-22 10:33:14 +07:00
|
|
|
s.mock.Ctx.SaveSession(userSession) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
2019-12-07 18:18:22 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FetchSuite) TearDownTest() {
|
|
|
|
s.mock.Close()
|
|
|
|
}
|
|
|
|
|
2020-06-21 20:40:37 +07:00
|
|
|
func setPreferencesExpectations(preferences UserInfo, provider *storage.MockProvider) {
|
2019-12-08 00:51:47 +07:00
|
|
|
provider.
|
2019-12-07 18:18:22 +07:00
|
|
|
EXPECT().
|
2020-01-06 06:03:16 +07:00
|
|
|
LoadPreferred2FAMethod(gomock.Eq("john")).
|
2019-12-07 18:18:22 +07:00
|
|
|
Return(preferences.Method, nil)
|
|
|
|
|
|
|
|
if preferences.HasU2F {
|
2019-12-08 00:51:47 +07:00
|
|
|
u2fData := []byte("abc")
|
|
|
|
provider.
|
|
|
|
EXPECT().
|
|
|
|
LoadU2FDeviceHandle(gomock.Eq("john")).
|
|
|
|
Return(u2fData, u2fData, nil)
|
|
|
|
} else {
|
|
|
|
provider.
|
|
|
|
EXPECT().
|
|
|
|
LoadU2FDeviceHandle(gomock.Eq("john")).
|
|
|
|
Return(nil, nil, storage.ErrNoU2FDeviceHandle)
|
2019-12-07 18:18:22 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if preferences.HasTOTP {
|
2019-12-08 00:51:47 +07:00
|
|
|
totpSecret := "secret"
|
|
|
|
provider.
|
|
|
|
EXPECT().
|
|
|
|
LoadTOTPSecret(gomock.Eq("john")).
|
|
|
|
Return(totpSecret, nil)
|
|
|
|
} else {
|
|
|
|
provider.
|
|
|
|
EXPECT().
|
|
|
|
LoadTOTPSecret(gomock.Eq("john")).
|
|
|
|
Return("", storage.ErrNoTOTPSecret)
|
2019-12-07 18:18:22 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-08 00:51:47 +07:00
|
|
|
func TestMethodSetToU2F(t *testing.T) {
|
2020-06-21 20:40:37 +07:00
|
|
|
table := []UserInfo{
|
2020-04-09 08:05:17 +07:00
|
|
|
{
|
2019-12-07 18:18:22 +07:00
|
|
|
Method: "totp",
|
|
|
|
},
|
2020-04-09 08:05:17 +07:00
|
|
|
{
|
2019-12-07 18:18:22 +07:00
|
|
|
Method: "u2f",
|
|
|
|
HasU2F: true,
|
|
|
|
HasTOTP: true,
|
|
|
|
},
|
2020-04-09 08:05:17 +07:00
|
|
|
{
|
2019-12-07 18:18:22 +07:00
|
|
|
Method: "u2f",
|
|
|
|
HasU2F: true,
|
|
|
|
HasTOTP: false,
|
|
|
|
},
|
2020-04-09 08:05:17 +07:00
|
|
|
{
|
2019-12-08 00:51:47 +07:00
|
|
|
Method: "mobile_push",
|
|
|
|
HasU2F: false,
|
|
|
|
HasTOTP: false,
|
|
|
|
},
|
2019-12-07 18:18:22 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, expectedPreferences := range table {
|
2019-12-08 00:51:47 +07:00
|
|
|
mock := mocks.NewMockAutheliaCtx(t)
|
2020-01-06 06:03:16 +07:00
|
|
|
// Set the initial user session.
|
2019-12-08 00:51:47 +07:00
|
|
|
userSession := mock.Ctx.GetSession()
|
2020-05-02 23:20:40 +07:00
|
|
|
userSession.Username = testUsername
|
2019-12-08 00:51:47 +07:00
|
|
|
userSession.AuthenticationLevel = 1
|
2020-04-22 10:33:14 +07:00
|
|
|
mock.Ctx.SaveSession(userSession) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
2019-12-08 00:51:47 +07:00
|
|
|
|
|
|
|
setPreferencesExpectations(expectedPreferences, mock.StorageProviderMock)
|
|
|
|
UserInfoGet(mock.Ctx)
|
2019-12-07 18:18:22 +07:00
|
|
|
|
2020-06-21 20:40:37 +07:00
|
|
|
actualPreferences := UserInfo{}
|
2019-12-08 00:51:47 +07:00
|
|
|
mock.GetResponseData(t, &actualPreferences)
|
2019-12-07 18:18:22 +07:00
|
|
|
|
2019-12-08 00:51:47 +07:00
|
|
|
t.Run("expected method", func(t *testing.T) {
|
|
|
|
assert.Equal(t, expectedPreferences.Method, actualPreferences.Method)
|
2019-12-07 18:18:22 +07:00
|
|
|
})
|
|
|
|
|
2019-12-08 00:51:47 +07:00
|
|
|
t.Run("registered u2f", func(t *testing.T) {
|
|
|
|
assert.Equal(t, expectedPreferences.HasU2F, actualPreferences.HasU2F)
|
2019-12-07 18:18:22 +07:00
|
|
|
})
|
|
|
|
|
2019-12-08 00:51:47 +07:00
|
|
|
t.Run("registered totp", func(t *testing.T) {
|
|
|
|
assert.Equal(t, expectedPreferences.HasTOTP, actualPreferences.HasTOTP)
|
2019-12-07 18:18:22 +07:00
|
|
|
})
|
2019-12-08 00:51:47 +07:00
|
|
|
mock.Close()
|
2019-12-07 18:18:22 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FetchSuite) TestShouldGetDefaultPreferenceIfNotInDB() {
|
|
|
|
s.mock.StorageProviderMock.
|
|
|
|
EXPECT().
|
2020-01-06 06:03:16 +07:00
|
|
|
LoadPreferred2FAMethod(gomock.Eq("john")).
|
2019-12-07 18:18:22 +07:00
|
|
|
Return("", nil)
|
|
|
|
|
|
|
|
s.mock.StorageProviderMock.
|
|
|
|
EXPECT().
|
|
|
|
LoadU2FDeviceHandle(gomock.Eq("john")).
|
2019-12-08 00:51:47 +07:00
|
|
|
Return(nil, nil, storage.ErrNoU2FDeviceHandle)
|
2019-12-07 18:18:22 +07:00
|
|
|
|
|
|
|
s.mock.StorageProviderMock.
|
|
|
|
EXPECT().
|
|
|
|
LoadTOTPSecret(gomock.Eq("john")).
|
2019-12-08 00:51:47 +07:00
|
|
|
Return("", storage.ErrNoTOTPSecret)
|
2019-12-07 18:18:22 +07:00
|
|
|
|
|
|
|
UserInfoGet(s.mock.Ctx)
|
2020-06-21 20:40:37 +07:00
|
|
|
s.mock.Assert200OK(s.T(), UserInfo{Method: "totp"})
|
2019-12-07 18:18:22 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FetchSuite) TestShouldReturnError500WhenStorageFailsToLoad() {
|
|
|
|
s.mock.StorageProviderMock.EXPECT().
|
2020-01-06 06:03:16 +07:00
|
|
|
LoadPreferred2FAMethod(gomock.Eq("john")).
|
2019-12-07 18:18:22 +07:00
|
|
|
Return("", fmt.Errorf("Failure"))
|
2019-12-08 00:51:47 +07:00
|
|
|
|
|
|
|
s.mock.StorageProviderMock.
|
|
|
|
EXPECT().
|
|
|
|
LoadU2FDeviceHandle(gomock.Eq("john"))
|
|
|
|
|
|
|
|
s.mock.StorageProviderMock.
|
|
|
|
EXPECT().
|
|
|
|
LoadTOTPSecret(gomock.Eq("john"))
|
|
|
|
|
2019-12-07 18:18:22 +07:00
|
|
|
UserInfoGet(s.mock.Ctx)
|
|
|
|
|
|
|
|
s.mock.Assert200KO(s.T(), "Operation failed.")
|
2019-12-08 00:51:47 +07:00
|
|
|
assert.Equal(s.T(), "Unable to load user information", s.mock.Hook.LastEntry().Message)
|
2019-12-07 18:18:22 +07:00
|
|
|
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFetchSuite(t *testing.T) {
|
|
|
|
suite.Run(t, &FetchSuite{})
|
|
|
|
}
|
|
|
|
|
|
|
|
type SaveSuite struct {
|
|
|
|
suite.Suite
|
|
|
|
mock *mocks.MockAutheliaCtx
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) SetupTest() {
|
|
|
|
s.mock = mocks.NewMockAutheliaCtx(s.T())
|
2020-01-06 06:03:16 +07:00
|
|
|
// Set the initial user session.
|
2019-12-07 18:18:22 +07:00
|
|
|
userSession := s.mock.Ctx.GetSession()
|
2020-05-02 23:20:40 +07:00
|
|
|
userSession.Username = testUsername
|
2019-12-07 18:18:22 +07:00
|
|
|
userSession.AuthenticationLevel = 1
|
2020-04-22 10:33:14 +07:00
|
|
|
s.mock.Ctx.SaveSession(userSession) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
2019-12-07 18:18:22 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) TearDownTest() {
|
|
|
|
s.mock.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) TestShouldReturnError500WhenNoBodyProvided() {
|
|
|
|
s.mock.Ctx.Request.SetBody(nil)
|
|
|
|
MethodPreferencePost(s.mock.Ctx)
|
|
|
|
|
|
|
|
s.mock.Assert200KO(s.T(), "Operation failed.")
|
|
|
|
assert.Equal(s.T(), "Unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message)
|
|
|
|
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) TestShouldReturnError500WhenMalformedBodyProvided() {
|
|
|
|
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\""))
|
|
|
|
MethodPreferencePost(s.mock.Ctx)
|
|
|
|
|
|
|
|
s.mock.Assert200KO(s.T(), "Operation failed.")
|
|
|
|
assert.Equal(s.T(), "Unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message)
|
|
|
|
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) TestShouldReturnError500WhenBadBodyProvided() {
|
|
|
|
s.mock.Ctx.Request.SetBody([]byte("{\"weird_key\":\"abc\"}"))
|
|
|
|
MethodPreferencePost(s.mock.Ctx)
|
|
|
|
|
|
|
|
s.mock.Assert200KO(s.T(), "Operation failed.")
|
|
|
|
assert.Equal(s.T(), "Unable to validate body: method: non zero value required", s.mock.Hook.LastEntry().Message)
|
|
|
|
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) TestShouldReturnError500WhenBadMethodProvided() {
|
|
|
|
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"abc\"}"))
|
|
|
|
MethodPreferencePost(s.mock.Ctx)
|
|
|
|
|
|
|
|
s.mock.Assert200KO(s.T(), "Operation failed.")
|
|
|
|
assert.Equal(s.T(), "Unknown method 'abc', it should be one of totp, u2f, mobile_push", s.mock.Hook.LastEntry().Message)
|
|
|
|
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) TestShouldReturnError500WhenDatabaseFailsToSave() {
|
|
|
|
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"u2f\"}"))
|
|
|
|
s.mock.StorageProviderMock.EXPECT().
|
2020-01-06 06:03:16 +07:00
|
|
|
SavePreferred2FAMethod(gomock.Eq("john"), gomock.Eq("u2f")).
|
2019-12-07 18:18:22 +07:00
|
|
|
Return(fmt.Errorf("Failure"))
|
|
|
|
|
|
|
|
MethodPreferencePost(s.mock.Ctx)
|
|
|
|
|
|
|
|
s.mock.Assert200KO(s.T(), "Operation failed.")
|
2020-01-06 06:03:16 +07:00
|
|
|
assert.Equal(s.T(), "Unable to save new preferred 2FA method: Failure", s.mock.Hook.LastEntry().Message)
|
2019-12-07 18:18:22 +07:00
|
|
|
assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SaveSuite) TestShouldReturn200WhenMethodIsSuccessfullySaved() {
|
|
|
|
s.mock.Ctx.Request.SetBody([]byte("{\"method\":\"u2f\"}"))
|
|
|
|
s.mock.StorageProviderMock.EXPECT().
|
2020-01-06 06:03:16 +07:00
|
|
|
SavePreferred2FAMethod(gomock.Eq("john"), gomock.Eq("u2f")).
|
2019-12-07 18:18:22 +07:00
|
|
|
Return(nil)
|
|
|
|
|
|
|
|
MethodPreferencePost(s.mock.Ctx)
|
|
|
|
|
|
|
|
assert.Equal(s.T(), 200, s.mock.Ctx.Response.StatusCode())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSaveSuite(t *testing.T) {
|
|
|
|
suite.Run(t, &SaveSuite{})
|
|
|
|
}
|