diff options
Diffstat (limited to 'internal/handlers/handler_change_password_test.go')
| -rw-r--r-- | internal/handlers/handler_change_password_test.go | 302 | 
1 files changed, 302 insertions, 0 deletions
diff --git a/internal/handlers/handler_change_password_test.go b/internal/handlers/handler_change_password_test.go new file mode 100644 index 000000000..3783fef8e --- /dev/null +++ b/internal/handlers/handler_change_password_test.go @@ -0,0 +1,302 @@ +package handlers + +import ( +	"encoding/json" +	"fmt" +	"testing" + +	"github.com/stretchr/testify/assert" +	"github.com/stretchr/testify/suite" +	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock" + +	"github.com/authelia/authelia/v4/internal/authentication" + +	"github.com/authelia/authelia/v4/internal/configuration/schema" +	"github.com/authelia/authelia/v4/internal/middlewares" +	"github.com/authelia/authelia/v4/internal/mocks" +) + +const ( +	testPasswordOld = "old_password123" +	testPasswordNew = "new_password456" +) + +type ChangePasswordSuite struct { +	suite.Suite +	mock *mocks.MockAutheliaCtx +} + +func (s *ChangePasswordSuite) SetupTest() { +	s.mock = mocks.NewMockAutheliaCtx(s.T()) +	userSession, err := s.mock.Ctx.GetSession() +	s.Assert().NoError(err) + +	userSession.Username = testUsername +	userSession.DisplayName = testUsername +	userSession.Emails[0] = testEmail +	userSession.AuthenticationMethodRefs.UsernameAndPassword = true +	s.Assert().NoError(s.mock.Ctx.SaveSession(userSession)) +} + +func (s *ChangePasswordSuite) TearDownTest() { +	s.mock.Close() +} + +func TestChangePasswordPOST_ShouldSucceedWithValidCredentials(t *testing.T) { +	mock := mocks.NewMockAutheliaCtx(t) + +	userSession, err := mock.Ctx.GetSession() +	assert.NoError(t, err) + +	userSession.Username = testUsername + +	assert.NoError(t, mock.Ctx.SaveSession(userSession)) + +	oldPassword := testPasswordOld +	newPassword := testPasswordNew + +	requestBody := changePasswordRequestBody{ +		OldPassword: oldPassword, +		NewPassword: newPassword, +	} + +	bodyBytes, err := json.Marshal(requestBody) +	assert.NoError(t, err) +	mock.Ctx.Request.SetBody(bodyBytes) + +	mock.Ctx.Providers.PasswordPolicy = middlewares.NewPasswordPolicyProvider(schema.PasswordPolicy{}) + +	mock.NotifierMock.EXPECT(). +		Send(mock.Ctx, gomock.Any(), "Password changed successfully", gomock.Any(), gomock.Any()). +		Return(nil) + +	mock.UserProviderMock.EXPECT(). +		ChangePassword(userSession.Username, oldPassword, newPassword). +		Return(nil) + +	mock.UserProviderMock.EXPECT(). +		GetDetails(testUsername). +		Return(&authentication.UserDetails{ +			Emails: []string{testEmail}, +		}, nil) + +	ChangePasswordPOST(mock.Ctx) + +	assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode()) +} + +func TestChangePasswordPOST_ShouldFailWhenPasswordPolicyNotMet(t *testing.T) { +	mock := mocks.NewMockAutheliaCtx(t) + +	userSession, err := mock.Ctx.GetSession() +	assert.NoError(t, err) + +	userSession.Username = testUsername + +	assert.NoError(t, mock.Ctx.SaveSession(userSession)) + +	oldPassword := testPasswordOld +	newPassword := "weak" + +	requestBody := changePasswordRequestBody{ +		OldPassword: oldPassword, +		NewPassword: newPassword, +	} + +	bodyBytes, err := json.Marshal(requestBody) +	assert.NoError(t, err) +	mock.Ctx.Request.SetBody(bodyBytes) + +	passwordPolicy := middlewares.NewPasswordPolicyProvider(schema.PasswordPolicy{ +		Standard: schema.PasswordPolicyStandard{ +			Enabled:          true, +			MinLength:        8, +			MaxLength:        64, +			RequireNumber:    true, +			RequireSpecial:   true, +			RequireUppercase: true, +			RequireLowercase: true, +		}, +	}) + +	mock.Ctx.Providers.PasswordPolicy = passwordPolicy + +	ChangePasswordPOST(mock.Ctx) + +	errResponse := mock.GetResponseError(t) + +	assert.Equal(t, "KO", errResponse.Status) +	assert.Equal(t, "Your supplied password does not meet the password policy requirements.", errResponse.Message) +} + +func TestChangePasswordPOST_ShouldFailWhenRequestBodyIsInvalid(t *testing.T) { +	mock := mocks.NewMockAutheliaCtx(t) + +	userSession, err := mock.Ctx.GetSession() +	assert.NoError(t, err) + +	userSession.Username = testUsername + +	assert.NoError(t, mock.Ctx.SaveSession(userSession)) + +	mock.Ctx.Request.SetBody([]byte(`{invalid json`)) + +	ChangePasswordPOST(mock.Ctx) + +	errResponse := mock.GetResponseError(t) +	assert.Equal(t, "KO", errResponse.Status) +	assert.Equal(t, messageUnableToChangePassword, errResponse.Message) +} + +func TestChangePasswordPOST_ShouldFailWhenOldPasswordIsIncorrect(t *testing.T) { +	mock := mocks.NewMockAutheliaCtx(t) + +	userSession, err := mock.Ctx.GetSession() +	assert.NoError(t, err) + +	userSession.Username = testUsername + +	assert.NoError(t, mock.Ctx.SaveSession(userSession)) + +	oldPassword := testPasswordOld +	newPassword := testPasswordNew + +	requestBody := changePasswordRequestBody{ +		OldPassword: oldPassword, +		NewPassword: newPassword, +	} + +	bodyBytes, err := json.Marshal(requestBody) +	assert.NoError(t, err) +	mock.Ctx.Request.SetBody(bodyBytes) + +	mock.Ctx.Providers.PasswordPolicy = middlewares.NewPasswordPolicyProvider(schema.PasswordPolicy{}) + +	mock.UserProviderMock.EXPECT(). +		ChangePassword(testUsername, oldPassword, newPassword). +		Return(authentication.ErrIncorrectPassword) + +	ChangePasswordPOST(mock.Ctx) + +	errResponse := mock.GetResponseError(t) +	assert.Equal(t, "KO", errResponse.Status) +	assert.Equal(t, messageIncorrectPassword, errResponse.Message) +} + +func TestChangePasswordPOST_ShouldFailWhenPasswordReuseIsNotAllowed(t *testing.T) { +	mock := mocks.NewMockAutheliaCtx(t) + +	userSession, err := mock.Ctx.GetSession() +	assert.NoError(t, err) + +	userSession.Username = testUsername + +	assert.NoError(t, mock.Ctx.SaveSession(userSession)) + +	oldPassword := testPasswordOld +	newPassword := testPasswordOld + +	requestBody := changePasswordRequestBody{ +		OldPassword: oldPassword, +		NewPassword: newPassword, +	} + +	bodyBytes, err := json.Marshal(requestBody) +	assert.NoError(t, err) +	mock.Ctx.Request.SetBody(bodyBytes) + +	mock.Ctx.Providers.PasswordPolicy = middlewares.NewPasswordPolicyProvider(schema.PasswordPolicy{}) + +	mock.UserProviderMock.EXPECT(). +		ChangePassword(testUsername, oldPassword, newPassword). +		Return(authentication.ErrPasswordWeak) + +	ChangePasswordPOST(mock.Ctx) + +	errResponse := mock.GetResponseError(t) +	assert.Equal(t, "KO", errResponse.Status) +	assert.Equal(t, messagePasswordWeak, errResponse.Message) +} + +func TestChangePasswordPOST_ShouldSucceedButLogErrorWhenUserHasNoEmail(t *testing.T) { +	mock := mocks.NewMockAutheliaCtx(t) + +	userSession, err := mock.Ctx.GetSession() +	assert.NoError(t, err) + +	userSession.Username = testUsername + +	assert.NoError(t, mock.Ctx.SaveSession(userSession)) + +	oldPassword := testPasswordOld +	newPassword := testPasswordNew + +	requestBody := changePasswordRequestBody{ +		OldPassword: oldPassword, +		NewPassword: newPassword, +	} + +	bodyBytes, err := json.Marshal(requestBody) +	assert.NoError(t, err) +	mock.Ctx.Request.SetBody(bodyBytes) + +	mock.Ctx.Providers.PasswordPolicy = middlewares.NewPasswordPolicyProvider(schema.PasswordPolicy{}) + +	mock.UserProviderMock.EXPECT(). +		ChangePassword(testUsername, oldPassword, newPassword). +		Return(nil) + +	mock.UserProviderMock.EXPECT(). +		GetDetails(testUsername). +		Return(&authentication.UserDetails{ +			Emails: []string{}, +		}, nil) + +	ChangePasswordPOST(mock.Ctx) + +	assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode()) +} + +func TestChangePasswordPOST_ShouldSucceedButLogErrorWhenNotificationFails(t *testing.T) { +	mock := mocks.NewMockAutheliaCtx(t) + +	userSession, err := mock.Ctx.GetSession() +	assert.NoError(t, err) + +	userSession.Username = testUsername + +	assert.NoError(t, mock.Ctx.SaveSession(userSession)) + +	oldPassword := testPasswordOld +	newPassword := testPasswordNew + +	requestBody := changePasswordRequestBody{ +		OldPassword: oldPassword, +		NewPassword: newPassword, +	} + +	bodyBytes, err := json.Marshal(requestBody) +	assert.NoError(t, err) +	mock.Ctx.Request.SetBody(bodyBytes) + +	mock.Ctx.Providers.PasswordPolicy = middlewares.NewPasswordPolicyProvider(schema.PasswordPolicy{}) + +	mock.UserProviderMock.EXPECT(). +		ChangePassword(testUsername, testPasswordOld, newPassword). +		Return(nil) + +	mock.UserProviderMock.EXPECT(). +		GetDetails(testUsername). +		Return(&authentication.UserDetails{ +			Emails: []string{testEmail}, +		}, nil) + +	mock.NotifierMock.EXPECT(). +		Send(mock.Ctx, gomock.Any(), "Password changed successfully", gomock.Any(), gomock.Any()). +		Return(fmt.Errorf("failed to send notification")) + +	ChangePasswordPOST(mock.Ctx) + +	assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode()) +}  | 
