diff options
| author | James Elliott <james-d-elliott@users.noreply.github.com> | 2022-03-28 12:26:30 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-03-28 12:26:30 +1100 |
| commit | 70ab8aab155149ac1c870fe6a3a9cd9fbe47b78f (patch) | |
| tree | 1709066b37a64fb7ba55c5cd616f541381b71105 /internal/handlers/handler_user_info_test.go | |
| parent | 2d8978c15a3d50df5e3c597efe51456271225bd2 (diff) | |
fix(web): show appropriate default and available methods (#2999)
This ensures that; the method set when a user does not have a preference is a method that is available, that if a user has a preferred method that is not available it is changed to an enabled method with preference put on methods the user has configured, that the frontend does not show the method selection option when only one method is available.
Diffstat (limited to 'internal/handlers/handler_user_info_test.go')
| -rw-r--r-- | internal/handlers/handler_user_info_test.go | 223 |
1 files changed, 219 insertions, 4 deletions
diff --git a/internal/handlers/handler_user_info_test.go b/internal/handlers/handler_user_info_test.go index b3b3ec646..d2fd256a7 100644 --- a/internal/handlers/handler_user_info_test.go +++ b/internal/handlers/handler_user_info_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/authelia/authelia/v4/internal/configuration/schema" "github.com/authelia/authelia/v4/internal/mocks" "github.com/authelia/authelia/v4/internal/model" ) @@ -41,7 +42,17 @@ type expectedResponse struct { err error } -func TestMethodSetToU2F(t *testing.T) { +type expectedResponseAlt struct { + description string + + db model.UserInfo + api *model.UserInfo + loadErr error + saveErr error + config *schema.Configuration +} + +func TestUserInfoEndpoint_SetCorrectMethod(t *testing.T) { expectedResponses := []expectedResponse{ { db: model.UserInfo{ @@ -89,6 +100,9 @@ func TestMethodSetToU2F(t *testing.T) { } mock := mocks.NewMockAutheliaCtx(t) + + mock.Ctx.Configuration.DuoAPI = &schema.DuoAPIConfiguration{} + // Set the initial user session. userSession := mock.Ctx.GetSession() userSession.Username = testUsername @@ -101,7 +115,7 @@ func TestMethodSetToU2F(t *testing.T) { LoadUserInfo(mock.Ctx, gomock.Eq("john")). Return(resp.db, resp.err) - UserInfoGet(mock.Ctx) + UserInfoGET(mock.Ctx) if resp.err == nil { t.Run("expected status code", func(t *testing.T) { @@ -123,6 +137,207 @@ func TestMethodSetToU2F(t *testing.T) { t.Run("registered totp", func(t *testing.T) { assert.Equal(t, resp.api.HasTOTP, actualPreferences.HasTOTP) }) + + t.Run("registered duo", func(t *testing.T) { + assert.Equal(t, resp.api.HasDuo, actualPreferences.HasDuo) + }) + } else { + t.Run("expected status code", func(t *testing.T) { + assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) + }) + + errResponse := mock.GetResponseError(t) + + assert.Equal(t, "KO", errResponse.Status) + assert.Equal(t, "Operation failed.", errResponse.Message) + } + + mock.Close() + } +} + +func TestUserInfoEndpoint_SetDefaultMethod(t *testing.T) { + expectedResponses := []expectedResponseAlt{ + { + description: "should set method to totp by default even when user doesn't have totp configured and no preferred method", + db: model.UserInfo{ + Method: "", + HasTOTP: false, + HasWebauthn: false, + HasDuo: false, + }, + api: &model.UserInfo{ + Method: "totp", + HasTOTP: false, + HasWebauthn: false, + HasDuo: false, + }, + config: &schema.Configuration{ + DuoAPI: &schema.DuoAPIConfiguration{}, + }, + loadErr: nil, + saveErr: nil, + }, + { + description: "should set method to duo by default when user has duo configured and no preferred method", + db: model.UserInfo{ + Method: "", + HasTOTP: false, + HasWebauthn: false, + HasDuo: true, + }, + api: &model.UserInfo{ + Method: "mobile_push", + HasTOTP: false, + HasWebauthn: false, + HasDuo: true, + }, + config: &schema.Configuration{ + DuoAPI: &schema.DuoAPIConfiguration{}, + }, + loadErr: nil, + saveErr: nil, + }, + { + description: "should set method to totp by default when user has duo configured and no preferred method but duo is not enabled", + db: model.UserInfo{ + Method: "", + HasTOTP: false, + HasWebauthn: false, + HasDuo: true, + }, + api: &model.UserInfo{ + Method: "totp", + HasTOTP: false, + HasWebauthn: false, + HasDuo: true, + }, + loadErr: nil, + saveErr: nil, + }, + { + description: "should set method to duo by default when user has duo configured and no preferred method", + db: model.UserInfo{ + Method: "", + HasTOTP: true, + HasWebauthn: true, + HasDuo: true, + }, + api: &model.UserInfo{ + Method: "webauthn", + HasTOTP: true, + HasWebauthn: true, + HasDuo: true, + }, + config: &schema.Configuration{ + TOTP: schema.TOTPConfiguration{ + Disable: true, + }, + DuoAPI: &schema.DuoAPIConfiguration{}, + }, + loadErr: nil, + saveErr: nil, + }, + { + description: "should default new users to totp if all enabled", + db: model.UserInfo{ + Method: "", + HasTOTP: false, + HasWebauthn: false, + HasDuo: false, + }, + api: &model.UserInfo{ + Method: "totp", + HasTOTP: true, + HasWebauthn: true, + HasDuo: true, + }, + config: &schema.Configuration{ + DuoAPI: &schema.DuoAPIConfiguration{}, + }, + loadErr: nil, + saveErr: errors.New("could not save"), + }, + } + + for _, resp := range expectedResponses { + if resp.api == nil { + resp.api = &resp.db + } + + mock := mocks.NewMockAutheliaCtx(t) + + if resp.config != nil { + mock.Ctx.Configuration = *resp.config + } + + // Set the initial user session. + userSession := mock.Ctx.GetSession() + userSession.Username = testUsername + userSession.AuthenticationLevel = 1 + err := mock.Ctx.SaveSession(userSession) + require.NoError(t, err) + + if resp.db.Method == "" { + gomock.InOrder( + mock.StorageMock. + EXPECT(). + LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")). + Return("", sql.ErrNoRows), + mock.StorageMock. + EXPECT(). + SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq("")). + Return(resp.saveErr), + mock.StorageMock. + EXPECT(). + LoadUserInfo(mock.Ctx, gomock.Eq("john")). + Return(resp.db, nil), + mock.StorageMock.EXPECT(). + SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)). + Return(resp.saveErr), + ) + } else { + gomock.InOrder( + mock.StorageMock. + EXPECT(). + LoadPreferred2FAMethod(mock.Ctx, gomock.Eq("john")). + Return(resp.db.Method, nil), + mock.StorageMock. + EXPECT(). + LoadUserInfo(mock.Ctx, gomock.Eq("john")). + Return(resp.db, nil), + mock.StorageMock.EXPECT(). + SavePreferred2FAMethod(mock.Ctx, gomock.Eq("john"), gomock.Eq(resp.api.Method)). + Return(resp.saveErr), + ) + } + + UserInfoPOST(mock.Ctx) + + if resp.loadErr == nil && resp.saveErr == nil { + t.Run(fmt.Sprintf("%s/%s", resp.description, "expected status code"), func(t *testing.T) { + assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) + }) + + actualPreferences := model.UserInfo{} + + mock.GetResponseData(t, &actualPreferences) + + t.Run(fmt.Sprintf("%s/%s", resp.description, "expected method"), func(t *testing.T) { + assert.Equal(t, resp.api.Method, actualPreferences.Method) + }) + + t.Run(fmt.Sprintf("%s/%s", resp.description, "registered webauthn"), func(t *testing.T) { + assert.Equal(t, resp.api.HasWebauthn, actualPreferences.HasWebauthn) + }) + + t.Run(fmt.Sprintf("%s/%s", resp.description, "registered totp"), func(t *testing.T) { + assert.Equal(t, resp.api.HasTOTP, actualPreferences.HasTOTP) + }) + + t.Run(fmt.Sprintf("%s/%s", resp.description, "registered duo"), func(t *testing.T) { + assert.Equal(t, resp.api.HasDuo, actualPreferences.HasDuo) + }) } else { t.Run("expected status code", func(t *testing.T) { assert.Equal(t, 200, mock.Ctx.Response.StatusCode()) @@ -143,7 +358,7 @@ func (s *FetchSuite) TestShouldReturnError500WhenStorageFailsToLoad() { LoadUserInfo(s.mock.Ctx, gomock.Eq("john")). Return(model.UserInfo{}, fmt.Errorf("failure")) - UserInfoGet(s.mock.Ctx) + UserInfoGET(s.mock.Ctx) s.mock.Assert200KO(s.T(), "Operation failed.") assert.Equal(s.T(), "unable to load user information: failure", s.mock.Hook.LastEntry().Message) @@ -205,7 +420,7 @@ func (s *SaveSuite) TestShouldReturnError500WhenBadMethodProvided() { MethodPreferencePost(s.mock.Ctx) s.mock.Assert200KO(s.T(), "Operation failed.") - assert.Equal(s.T(), "unknown method 'abc', it should be one of totp, webauthn, mobile_push", s.mock.Hook.LastEntry().Message) + assert.Equal(s.T(), "unknown or unavailable method 'abc', it should be one of totp, webauthn", s.mock.Hook.LastEntry().Message) assert.Equal(s.T(), logrus.ErrorLevel, s.mock.Hook.LastEntry().Level) } |
