diff --git a/server/resolvers/reset_password.go b/server/resolvers/reset_password.go index 5a498c8..8380fab 100644 --- a/server/resolvers/reset_password.go +++ b/server/resolvers/reset_password.go @@ -35,6 +35,10 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput) return res, fmt.Errorf(`passwords don't match`) } + if !utils.IsValidPassword(params.Password) { + return res, fmt.Errorf(`password is not valid. It needs to be at least 6 characters long and contain at least one number, one uppercase letter, one lowercase letter and one special character`) + } + // verify if token exists in db hostname := utils.GetHost(gc) claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email) diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index 749f5a1..76d7175 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -40,6 +40,10 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR return res, fmt.Errorf(`password and confirm password does not match`) } + if !utils.IsValidPassword(params.Password) { + return res, fmt.Errorf(`password is not valid. It needs to be at least 6 characters long and contain at least one number, one uppercase letter, one lowercase letter and one special character`) + } + params.Email = strings.ToLower(params.Email) if !utils.IsValidEmail(params.Email) { diff --git a/server/test/magic_link_login_test.go b/server/test/magic_link_login_test.go index 95e0712..a38a802 100644 --- a/server/test/magic_link_login_test.go +++ b/server/test/magic_link_login_test.go @@ -6,6 +6,7 @@ import ( "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/envstore" "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/resolvers" "github.com/stretchr/testify/assert" @@ -16,11 +17,17 @@ func magicLinkLoginTests(t *testing.T, s TestSetup) { t.Run(`should login with magic link`, func(t *testing.T) { req, ctx := createContext(s) email := "magic_link_login." + s.TestInfo.Email - + envstore.EnvStoreObj.UpdateEnvVariable(constants.BoolStoreIdentifier, constants.EnvKeyDisableSignUp, true) _, err := resolvers.MagicLinkLoginResolver(ctx, model.MagicLinkLoginInput{ Email: email, }) - assert.Nil(t, err) + assert.NotNil(t, err, "signup disabled") + + envstore.EnvStoreObj.UpdateEnvVariable(constants.BoolStoreIdentifier, constants.EnvKeyDisableSignUp, false) + _, err = resolvers.MagicLinkLoginResolver(ctx, model.MagicLinkLoginInput{ + Email: email, + }) + assert.Nil(t, err, "signup should be successful") verificationRequest, err := db.Provider.GetVerificationRequestByEmail(email, constants.VerificationTypeMagicLinkLogin) verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{ diff --git a/server/test/reset_password_test.go b/server/test/reset_password_test.go index 6d0ce20..75ad76b 100644 --- a/server/test/reset_password_test.go +++ b/server/test/reset_password_test.go @@ -43,6 +43,14 @@ func resetPasswordTest(t *testing.T, s TestSetup) { ConfirmPassword: "test1", }) + assert.NotNil(t, err, "invalid password") + + _, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{ + Token: verificationRequest.Token, + Password: "Test@1234", + ConfirmPassword: "Test@1234", + }) + assert.Nil(t, err, "password changed successfully") cleanData(email) diff --git a/server/test/signup_test.go b/server/test/signup_test.go index 60af8eb..4c40da3 100644 --- a/server/test/signup_test.go +++ b/server/test/signup_test.go @@ -5,6 +5,7 @@ import ( "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/envstore" "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/resolvers" "github.com/stretchr/testify/assert" @@ -20,14 +21,30 @@ func signupTests(t *testing.T, s TestSetup) { Password: s.TestInfo.Password, ConfirmPassword: s.TestInfo.Password + "s", }) - assert.NotNil(t, err, "invalid password errors") + assert.NotNil(t, err, "invalid password") + res, err = resolvers.SignupResolver(ctx, model.SignUpInput{ + Email: email, + Password: "test", + ConfirmPassword: "test", + }) + assert.NotNil(t, err, "invalid password") + + envstore.EnvStoreObj.UpdateEnvVariable(constants.BoolStoreIdentifier, constants.EnvKeyDisableSignUp, true) res, err = resolvers.SignupResolver(ctx, model.SignUpInput{ Email: email, Password: s.TestInfo.Password, ConfirmPassword: s.TestInfo.Password, }) + assert.NotNil(t, err, "singup disabled") + envstore.EnvStoreObj.UpdateEnvVariable(constants.BoolStoreIdentifier, constants.EnvKeyDisableSignUp, false) + res, err = resolvers.SignupResolver(ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.Nil(t, err, "signup should be successful") user := *res.User assert.Equal(t, email, user.Email) assert.Nil(t, res.AccessToken, "access token should be nil") diff --git a/server/test/test.go b/server/test/test.go index 6c39cc6..ff0bdb3 100644 --- a/server/test/test.go +++ b/server/test/test.go @@ -68,7 +68,7 @@ func createContext(s TestSetup) (*http.Request, context.Context) { func testSetup() TestSetup { testData := TestData{ Email: fmt.Sprintf("%d_authorizer_tester@yopmail.com", time.Now().Unix()), - Password: "test", + Password: "Test@123", } envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnvPath, "../../.env.sample") diff --git a/server/test/validator_test.go b/server/test/validator_test.go index 1e6c9e5..9c4b51a 100644 --- a/server/test/validator_test.go +++ b/server/test/validator_test.go @@ -41,3 +41,11 @@ func TestIsValidIdentifier(t *testing.T) { assert.True(t, utils.IsValidVerificationIdentifier(constants.VerificationTypeUpdateEmail), "it should be valid identifier") assert.True(t, utils.IsValidVerificationIdentifier(constants.VerificationTypeForgotPassword), "it should be valid identifier") } + +func TestIsValidPassword(t *testing.T) { + assert.False(t, utils.IsValidPassword("test"), "it should be invalid password") + assert.False(t, utils.IsValidPassword("Te@1"), "it should be invalid password") + assert.False(t, utils.IsValidPassword("n*rp7GGTd29V{xx%{pDb@7n{](SD.!+.Mp#*$EHDGk&$pAMf7e#432Sg,Gr](j3n]jV/3F8BJJT+9u9{q=8zK:8u!rpQBaXJp%A+7r!jQj)M(vC$UX,h;;WKm$U6i#7dBnC&2ryKzKd+(y&=Ud)hErT/j;v3t..CM).8nS)9qLtV7pmP;@2QuzDyGfL7KB()k:BpjAGL@bxD%r5gcBfh7$&wutk!wzMfPFY#nkjjqyZbEHku,{jc;gvbYq2)3w=KExnYz9Vbv:;*;?f##faxkULdMpmm&yEfePixzx+[{[38zGN;3TzF;6M#Xy_tMtx:yK*n$bc(bPyGz%EYkC&]ttUF@#aZ%$QZ:u!icF@+"), "it should be invalid password") + assert.False(t, utils.IsValidPassword("test@123"), "it should be invalid password") + assert.True(t, utils.IsValidPassword("Test@123"), "it should be valid password") +} diff --git a/server/utils/validator.go b/server/utils/validator.go index 8ba840f..f3ac062 100644 --- a/server/utils/validator.go +++ b/server/utils/validator.go @@ -86,3 +86,35 @@ func IsStringArrayEqual(a, b []string) bool { } return true } + +// ValidatePassword to validate the password against the following policy +// min char length: 6 +// max char length: 36 +// at least one upper case letter +// at least one lower case letter +// at least one digit +// at least one special character +func IsValidPassword(password string) bool { + if len(password) < 6 || len(password) > 36 { + return false + } + + hasUpperCase := false + hasLowerCase := false + hasDigit := false + hasSpecialChar := false + + for _, char := range password { + if char >= 'A' && char <= 'Z' { + hasUpperCase = true + } else if char >= 'a' && char <= 'z' { + hasLowerCase = true + } else if char >= '0' && char <= '9' { + hasDigit = true + } else { + hasSpecialChar = true + } + } + + return hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar +}