github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/it/impl_resetpassword_test.go (about) 1 /* 2 * Copyright (c) 2020-present unTill Pro, Ltd. 3 */ 4 5 package sys_it 6 7 import ( 8 "fmt" 9 "testing" 10 "time" 11 12 "github.com/voedger/voedger/pkg/irates" 13 "github.com/voedger/voedger/pkg/istructs" 14 "github.com/voedger/voedger/pkg/istructsmem" 15 "github.com/voedger/voedger/pkg/sys/verifier" 16 coreutils "github.com/voedger/voedger/pkg/utils" 17 it "github.com/voedger/voedger/pkg/vit" 18 ) 19 20 func TestBasicUsage_ResetPassword(t *testing.T) { 21 vit := it.NewVIT(t, &it.SharedConfig_App1) 22 defer vit.TearDown() 23 loginName := vit.NextName() + "@123.com" 24 login := vit.SignUp(loginName, "1", istructs.AppQName_test1_app1) 25 vit.SignIn(login) 26 27 profileWSID := istructs.WSID(0) 28 token, code := InitiateEmailVerificationFunc(vit, func() *coreutils.FuncResponse { 29 body := fmt.Sprintf(`{"args":{"AppName":"%s","Email":"%s"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, istructs.AppQName_test1_app1, login.Name) 30 resp := vit.PostApp(istructs.AppQName_sys_registry, login.PseudoProfileWSID, "q.registry.InitiateResetPasswordByEmail", body) // null auth policy 31 32 // here in test we're actually know the profileWSID. But in the realife we don't. So let's show how it should be got 33 profileWSID = istructs.WSID(resp.SectionRow()[1].(float64)) 34 return resp 35 }) 36 37 // sys/registry/pseudo-profile-wsid/q.registry.IssueVerifiedValueTokenForResetPassword 38 body := fmt.Sprintf(`{"args":{"VerificationToken":"%s","VerificationCode":"%s","ProfileWSID":%d,"AppName":"%s"},"elements":[{"fields":["VerifiedValueToken"]}]}`, token, code, profileWSID, 39 istructs.AppQName_test1_app1) 40 resp := vit.PostApp(istructs.AppQName_sys_registry, login.PseudoProfileWSID, "q.registry.IssueVerifiedValueTokenForResetPassword", body) // null auth policy 41 verifiedValueToken := resp.SectionRow()[0].(string) 42 43 // sys/registry/pseudo-profile-wsid/c.registry.ResetPasswordByEmail 44 newPwd := "newPwd" 45 body = fmt.Sprintf(`{"args":{"AppName":"%s"},"unloggedArgs":{"Email":"%s","NewPwd":"%s"}}`, istructs.AppQName_test1_app1, verifiedValueToken, newPwd) 46 vit.PostApp(istructs.AppQName_sys_registry, login.PseudoProfileWSID, "c.registry.ResetPasswordByEmail", body) // null auth policy 47 48 // expect no errors on login with new password 49 login.Pwd = newPwd 50 vit.SignIn(login) 51 } 52 53 func TestIntiateResetPasswordErrors(t *testing.T) { 54 vit := it.NewVIT(t, &it.SharedConfig_App1) 55 defer vit.TearDown() 56 prn := vit.GetPrincipal(istructs.AppQName_test1_app1, it.TestEmail) 57 58 t.Run("400 bad request on bad appQName", func(t *testing.T) { 59 body := fmt.Sprintf(`{"args":{"AppName":"wrong app","Email":"%s"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, prn.Name) 60 vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.InitiateResetPasswordByEmail", body, coreutils.Expect400()).Println() 61 }) 62 63 // note: test "called in non-AppWS" is senceless because now func is taken from the workspace -> 400 bad request + "func does not exist in the workspace" anyway 64 65 t.Run("400 bad request on an unknown login", func(t *testing.T) { 66 body := fmt.Sprintf(`{"args":{"AppName":"%s","Email":"unknown"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, istructs.AppQName_test1_app1) 67 vit.PostApp(istructs.AppQName_sys_registry, coreutils.GetPseudoWSID(istructs.NullWSID, "unknown", istructs.MainClusterID), "q.registry.InitiateResetPasswordByEmail", body, coreutils.Expect400()).Println() 68 }) 69 } 70 71 func TestIssueResetPasswordTokenErrors(t *testing.T) { 72 vit := it.NewVIT(t, &it.SharedConfig_App1) 73 defer vit.TearDown() 74 prn := vit.GetPrincipal(istructs.AppQName_test1_app1, it.TestEmail) 75 76 t.Run("400 bad request on an unknown login", func(t *testing.T) { 77 unknownLogin := "unknown" 78 pseudoWSID := coreutils.GetPseudoWSID(istructs.NullWSID, unknownLogin, istructs.MainClusterID) 79 body := fmt.Sprintf(`{"args":{"AppName":"%s","Email":"%s"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, istructs.AppQName_test1_app1, unknownLogin) 80 vit.PostApp(istructs.AppQName_sys_registry, pseudoWSID, "q.registry.InitiateResetPasswordByEmail", body, coreutils.Expect400()).Println() 81 }) 82 83 profileWSID := istructs.WSID(0) 84 token, code := InitiateEmailVerificationFunc(vit, func() *coreutils.FuncResponse { 85 body := fmt.Sprintf(`{"args":{"AppName":"%s","Email":"%s"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, istructs.AppQName_test1_app1, prn.Name) 86 resp := vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.InitiateResetPasswordByEmail", body) 87 profileWSID = istructs.WSID(resp.SectionRow()[1].(float64)) 88 return resp 89 }) 90 91 t.Run("400 bad request on bad appQName", func(t *testing.T) { 92 body := fmt.Sprintf(`{"args":{"VerificationToken":"%s","VerificationCode":"%s","ProfileWSID":%d,"AppName":"wrong app"},"elements":[{"fields":["VerifiedValueToken"]}]}`, 93 token, code, profileWSID) 94 // note: was at profileWSID. It does not works since https://github.com/voedger/voedger/issues/1311 95 // because sys/registry:profileWSID workspace is not initialized -> call at pseudoProfileWSID 96 vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.IssueVerifiedValueTokenForResetPassword", body, coreutils.Expect400()).Println() 97 }) 98 } 99 100 func TestResetPasswordLimits(t *testing.T) { 101 t.Skip("wait for https://github.com/voedger/voedger/issues/2090") 102 vit := it.NewVIT(t, &it.SharedConfig_App1) 103 defer vit.TearDown() 104 prn := vit.GetPrincipal(istructs.AppQName_test1_app1, it.TestEmail) 105 var ( 106 profileWSID istructs.WSID 107 token string 108 code string 109 ) 110 111 t.Run("InitiateResetPasswordByEmail", func(t *testing.T) { 112 // mock rate limits 113 rateLimitName_InitiateEmailVerification := istructsmem.GetFunctionRateLimitName(verifier.QNameQueryInitiateEmailVerification, istructs.RateLimitKind_byWorkspace) 114 vit.MockBuckets(istructs.AppQName_test1_app1, rateLimitName_InitiateEmailVerification, irates.BucketState{ 115 Period: time.Minute, 116 MaxTokensPerPeriod: 1, 117 }) 118 119 // 1st call -> ok, do not store the code 120 _, _ = InitiateEmailVerificationFunc(vit, func() *coreutils.FuncResponse { 121 body := fmt.Sprintf(`{"args":{"AppName":"%s","Email":"%s"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, istructs.AppQName_test1_app1, prn.Name) 122 return vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.InitiateResetPasswordByEmail", body) 123 }) 124 125 // 2nd call -> limit exceeded 126 body := fmt.Sprintf(`{"args":{"AppName":"%s","Email":"%s"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, istructs.AppQName_test1_app1, prn.Name) 127 vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.InitiateResetPasswordByEmail", body, coreutils.Expect429()) 128 129 // proceed to the next minute to restore rates 130 vit.TimeAdd(time.Minute) 131 132 // call again to get actual token and code 133 token, code = InitiateEmailVerificationFunc(vit, func() *coreutils.FuncResponse { 134 body := fmt.Sprintf(`{"args":{"AppName":"%s","Email":"%s"},"elements":[{"fields":["VerificationToken","ProfileWSID"]}]}`, istructs.AppQName_test1_app1, prn.Name) 135 resp := vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.InitiateResetPasswordByEmail", body) 136 137 // here in test we're actually know the profileWSID. But in the realife we don't. So let's show how it should be got: 138 // q.sys.InitiateResetPasswordByEmail returns it 139 profileWSID = istructs.WSID(resp.SectionRow()[1].(float64)) 140 return resp 141 }) 142 }) 143 144 t.Run("IssueVerifiedValueTokenForResetPassword", func(t *testing.T) { 145 // mock rate limits 146 rateLimitName_IssueVerifiedValueToken := istructsmem.GetFunctionRateLimitName(verifier.QNameQueryIssueVerifiedValueToken, istructs.RateLimitKind_byWorkspace) 147 vit.MockBuckets(istructs.AppQName_test1_app1, rateLimitName_IssueVerifiedValueToken, irates.BucketState{ 148 Period: time.Minute, 149 MaxTokensPerPeriod: 1, 150 }) 151 152 wrongCode := code + "1" 153 wrongCodeBody := fmt.Sprintf(`{"args":{"VerificationToken":"%s","VerificationCode":"%s","ProfileWSID":%d,"AppName":"%s"},"elements":[{"fields":["VerifiedValueToken"]}]}`, token, wrongCode, profileWSID, 154 istructs.AppQName_test1_app1) 155 156 // 1st call with wrong code -> 400 bad request 157 vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.IssueVerifiedValueTokenForResetPassword", wrongCodeBody, coreutils.Expect400()) 158 159 // 2nd call with wrong code -> mocked limit exceeded, 429 Too many reuqets 160 vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.IssueVerifiedValueTokenForResetPassword", wrongCodeBody, coreutils.Expect429()) 161 162 // next calls with correct code -> 429 anyway 163 goodCodeBody := fmt.Sprintf(`{"args":{"VerificationToken":"%s","VerificationCode":"%s","ProfileWSID":%d,"AppName":"%s"},"elements":[{"fields":["VerifiedValueToken"]}]}`, token, code, profileWSID, 164 istructs.AppQName_test1_app1) 165 vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.IssueVerifiedValueTokenForResetPassword", goodCodeBody, coreutils.Expect429()) 166 167 // proceed to the next minute to restore rates 168 vit.TimeAdd(time.Minute) 169 170 // expect no errors now 171 vit.PostApp(istructs.AppQName_sys_registry, prn.PseudoProfileWSID, "q.registry.IssueVerifiedValueTokenForResetPassword", goodCodeBody) 172 173 }) 174 }