forked from grassrootseconomics/visedriver
		
	Merge branch 'master' into lash/purify-more
This commit is contained in:
		
						commit
						379d98ccd5
					
				| @ -7,7 +7,7 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/logging" | ||||
| ) | ||||
| 
 | ||||
| // DataType is a subprefix value used in association with vise/db.DATATYPE_USERDATA. 
 | ||||
| // DataType is a subprefix value used in association with vise/db.DATATYPE_USERDATA.
 | ||||
| //
 | ||||
| // All keys are used only within the context of a single account. Unless otherwise specified, the user context is the session id.
 | ||||
| //
 | ||||
| @ -55,6 +55,8 @@ const ( | ||||
| 	DATA_ACTIVE_DECIMAL | ||||
| 	// EVM address of the currently active voucher
 | ||||
| 	DATA_ACTIVE_ADDRESS | ||||
| 	//Holds count of the number of incorrect PIN attempts
 | ||||
| 	DATA_INCORRECT_PIN_ATTEMPTS | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  | ||||
| @ -6,9 +6,13 @@ import ( | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
| 
 | ||||
| // Define the regex pattern as a constant
 | ||||
| const ( | ||||
| 	// Define the regex pattern as a constant
 | ||||
| 	pinPattern = `^\d{4}$` | ||||
| 
 | ||||
| 	//Allowed incorrect  PIN attempts
 | ||||
| 	AllowedPINAttempts = uint8(3) | ||||
| 	 | ||||
| ) | ||||
| 
 | ||||
| // checks whether the given input is a 4 digit number
 | ||||
|  | ||||
| @ -128,6 +128,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn | ||||
| 	ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement) | ||||
| 	ls.DbRs.AddLocalFunc("update_all_profile_items", ussdHandlers.UpdateAllProfileItems) | ||||
| 	ls.DbRs.AddLocalFunc("set_back", ussdHandlers.SetBack) | ||||
| 	ls.DbRs.AddLocalFunc("show_blocked_account", ussdHandlers.ShowBlockedAccount) | ||||
| 
 | ||||
| 	return ussdHandlers, nil | ||||
| } | ||||
|  | ||||
| @ -734,11 +734,23 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res | ||||
| 			if h.st.MatchFlag(flag_account_authorized, false) { | ||||
| 				res.FlagReset = append(res.FlagReset, flag_incorrect_pin) | ||||
| 				res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) | ||||
| 				err := h.resetIncorrectPINAttempts(ctx, sessionId) | ||||
| 				if err != nil { | ||||
| 					return res, err | ||||
| 				} | ||||
| 			} else { | ||||
| 				res.FlagSet = append(res.FlagSet, flag_allow_update) | ||||
| 				res.FlagReset = append(res.FlagReset, flag_account_authorized) | ||||
| 				err := h.resetIncorrectPINAttempts(ctx, sessionId) | ||||
| 				if err != nil { | ||||
| 					return res, err | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			err := h.incrementIncorrectPINAttempts(ctx, sessionId) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 			} | ||||
| 			res.FlagSet = append(res.FlagSet, flag_incorrect_pin) | ||||
| 			res.FlagReset = append(res.FlagReset, flag_account_authorized) | ||||
| 			return res, nil | ||||
| @ -752,8 +764,34 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res | ||||
| // ResetIncorrectPin resets the incorrect pin flag  after a new PIN attempt.
 | ||||
| func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") | ||||
| 	flag_account_blocked, _ := h.flagManager.GetFlag("flag_account_blocked") | ||||
| 
 | ||||
| 	sessionId, ok := ctx.Value("SessionId").(string) | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	res.FlagReset = append(res.FlagReset, flag_incorrect_pin) | ||||
| 
 | ||||
| 	currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS) | ||||
| 	if err != nil { | ||||
| 		if !db.IsNotFound(err) { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} | ||||
| 	pinAttemptsValue, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64) | ||||
| 	remainingPINAttempts := common.AllowedPINAttempts - uint8(pinAttemptsValue) | ||||
| 	if remainingPINAttempts == 0 { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_account_blocked) | ||||
| 		return res, nil | ||||
| 	} | ||||
| 	if remainingPINAttempts < common.AllowedPINAttempts { | ||||
| 		res.Content = strconv.Itoa(int(remainingPINAttempts)) | ||||
| 	} | ||||
| 
 | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| @ -840,6 +878,16 @@ func (h *Handlers) QuitWithHelp(ctx context.Context, sym string, input []byte) ( | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // ShowBlockedAccount displays a message after an account has been blocked and how to reach support.
 | ||||
| func (h *Handlers) ShowBlockedAccount(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| 	code := codeFromCtx(ctx) | ||||
| 	l := gotext.NewLocale(translationDir, code) | ||||
| 	l.AddDomain("default") | ||||
| 	res.Content = l.Get("Your account has been locked. For help on how to unblock your account, contact support at: 0757628885") | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // VerifyYob verifies the length of the given input.
 | ||||
| func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| @ -2075,3 +2123,53 @@ func (h *Handlers) UpdateAllProfileItems(ctx context.Context, sym string, input | ||||
| 	} | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // incrementIncorrectPINAttempts keeps track of the number of incorrect PIN attempts
 | ||||
| func (h *Handlers) incrementIncorrectPINAttempts(ctx context.Context, sessionId string) error { | ||||
| 	var pinAttemptsCount uint8 | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS) | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			//First time Wrong PIN attempt: initialize with a count of 1
 | ||||
| 			pinAttemptsCount = 1 | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(pinAttemptsCount)))) | ||||
| 			if err != nil { | ||||
| 				logg.ErrorCtxf(ctx, "failed to write incorrect PIN attempts ", "key", common.DATA_INCORRECT_PIN_ATTEMPTS, "value", currentWrongPinAttempts, "error", err) | ||||
| 				return err | ||||
| 			} | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	pinAttemptsValue, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64) | ||||
| 	pinAttemptsCount = uint8(pinAttemptsValue) + 1 | ||||
| 
 | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(pinAttemptsCount)))) | ||||
| 	if err != nil { | ||||
| 		logg.ErrorCtxf(ctx, "failed to write incorrect PIN attempts ", "key", common.DATA_INCORRECT_PIN_ATTEMPTS, "value", pinAttemptsCount, "error", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // resetIncorrectPINAttempts resets the number of incorrect PIN attempts after a correct PIN entry
 | ||||
| func (h *Handlers) resetIncorrectPINAttempts(ctx context.Context, sessionId string) error { | ||||
| 	store := h.userdataStore | ||||
| 	currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS) | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| 	currentWrongPinAttemptsCount, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64) | ||||
| 	if currentWrongPinAttemptsCount <= uint64(common.AllowedPINAttempts) { | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("0"))) | ||||
| 		if err != nil { | ||||
| 			logg.ErrorCtxf(ctx, "failed to reset incorrect PIN attempts ", "key", common.DATA_INCORRECT_PIN_ATTEMPTS, "value", common.AllowedPINAttempts, "error", err) | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"path" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| @ -907,37 +908,79 @@ func TestResetAccountAuthorized(t *testing.T) { | ||||
| } | ||||
| 
 | ||||
| func TestIncorrectPinReset(t *testing.T) { | ||||
| 	sessionId := "session123" | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	fm, err := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") | ||||
| 	flag_account_blocked, _ := fm.parser.GetFlag("flag_account_blocked") | ||||
| 
 | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 
 | ||||
| 	// Define test cases
 | ||||
| 	tests := []struct { | ||||
| 		name           string | ||||
| 		input          []byte | ||||
| 		attempts       uint8 | ||||
| 		expectedResult resource.Result | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:  "Test incorrect pin reset", | ||||
| 			name:  "Test when incorrect PIN attempts is 2", | ||||
| 			input: []byte(""), | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagReset: []uint32{flag_incorrect_pin}, | ||||
| 				Content:   "1", //Expected remaining PIN attempts
 | ||||
| 			}, | ||||
| 			attempts: 2, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:  "Test incorrect pin reset when incorrect PIN attempts is 1", | ||||
| 			input: []byte(""), | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagReset: []uint32{flag_incorrect_pin}, | ||||
| 				Content:   "2", //Expected remaining PIN attempts
 | ||||
| 			}, | ||||
| 			attempts: 1, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:  "Test incorrect pin reset when incorrect PIN attempts is 1", | ||||
| 			input: []byte(""), | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagReset: []uint32{flag_incorrect_pin}, | ||||
| 				Content:   "2", //Expected remaining PIN attempts
 | ||||
| 			}, | ||||
| 			attempts: 1, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:  "Test incorrect pin reset when incorrect PIN attempts is 3(account expected to be blocked)", | ||||
| 			input: []byte(""), | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagReset: []uint32{flag_incorrect_pin}, | ||||
| 				FlagSet:   []uint32{flag_account_blocked}, | ||||
| 			}, | ||||
| 			attempts: 3, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 
 | ||||
| 			if err := store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(tt.attempts)))); err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 
 | ||||
| 			// Create the Handlers instance with the mock flag manager
 | ||||
| 			h := &Handlers{ | ||||
| 				flagManager: fm.parser, | ||||
| 				flagManager:   fm.parser, | ||||
| 				userdataStore: store, | ||||
| 			} | ||||
| 
 | ||||
| 			// Call the method
 | ||||
| 			res, err := h.ResetIncorrectPin(context.Background(), "reset_incorrect_pin", tt.input) | ||||
| 			res, err := h.ResetIncorrectPin(ctx, "reset_incorrect_pin", tt.input) | ||||
| 			if err != nil { | ||||
| 				t.Error(err) | ||||
| 			} | ||||
| @ -2190,3 +2233,55 @@ func TestGetVoucherDetails(t *testing.T) { | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, expectedResult, res) | ||||
| } | ||||
| 
 | ||||
| func TestCountIncorrectPINAttempts(t *testing.T) { | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	sessionId := "session123" | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	attempts := uint8(2) | ||||
| 
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: store, | ||||
| 	} | ||||
| 	err := store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(attempts)))) | ||||
| 	if err != nil { | ||||
| 		t.Logf(err.Error()) | ||||
| 	} | ||||
| 	err = h.incrementIncorrectPINAttempts(ctx, sessionId) | ||||
| 	if err != nil { | ||||
| 		t.Logf(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	attemptsAfterCount, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS) | ||||
| 	if err != nil { | ||||
| 		t.Logf(err.Error()) | ||||
| 	} | ||||
| 	pinAttemptsValue, _ := strconv.ParseUint(string(attemptsAfterCount), 0, 64) | ||||
| 	pinAttemptsCount := uint8(pinAttemptsValue) | ||||
| 	expectedAttempts := attempts + 1 | ||||
| 	assert.Equal(t, pinAttemptsCount, expectedAttempts) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestResetIncorrectPINAttempts(t *testing.T) { | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	sessionId := "session123" | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 
 | ||||
| 	err := store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("2"))) | ||||
| 	if err != nil { | ||||
| 		t.Logf(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: store, | ||||
| 	} | ||||
| 	h.resetIncorrectPINAttempts(ctx, sessionId) | ||||
| 	incorrectAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		t.Logf(err.Error()) | ||||
| 	} | ||||
| 	assert.Equal(t, "0", string(incorrectAttempts)) | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -54,7 +54,7 @@ | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1235", | ||||
|                     "expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" | ||||
|                     "expectedContent": "Incorrect PIN. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
| @ -95,7 +95,7 @@ | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1235", | ||||
|                     "expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" | ||||
|                     "expectedContent": "Incorrect PIN. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
| @ -107,8 +107,7 @@ | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                      "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                      | ||||
|                     "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
| @ -141,7 +140,7 @@ | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1235", | ||||
|                     "expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" | ||||
|                     "expectedContent": "Incorrect PIN. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
| @ -153,8 +152,7 @@ | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                      "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                      | ||||
|                     "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
| @ -195,7 +193,7 @@ | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent":  "Enter your year of birth\n0:Back" | ||||
|                     "expectedContent": "Enter your year of birth\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1940", | ||||
| @ -258,7 +256,6 @@ | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|                 | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
| @ -443,10 +440,4 @@ | ||||
|             ] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
|        | ||||
|          | ||||
|         | ||||
|          | ||||
|      | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										2
									
								
								services/registration/blocked_account.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/blocked_account.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| LOAD show_blocked_account 0 | ||||
| HALT  | ||||
| @ -1 +1 @@ | ||||
| Incorrect PIN | ||||
| Incorrect PIN. You have: {{.reset_incorrect}} remaining attempt(s). | ||||
| @ -1,5 +1,7 @@ | ||||
| LOAD reset_incorrect 0 | ||||
| RELOAD reset_incorrect | ||||
| MAP reset_incorrect | ||||
| CATCH blocked_account flag_account_blocked 1 | ||||
| MOUT retry 1 | ||||
| MOUT quit 9 | ||||
| HALT | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| PIN ulioeka sio sahihi | ||||
| PIN ulioeka sio sahihi, una majaribio: {{.reset_incorrect}} yaliyobaki | ||||
| @ -10,6 +10,9 @@ msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!" | ||||
| msgid "For more help, please call: 0757628885" | ||||
| msgstr "Kwa usaidizi zaidi, piga: 0757628885" | ||||
| 
 | ||||
| msgid "Your account has been locked. For help on how to unblock your account, contact support at: 0757628885" | ||||
| msgstr "Akaunti yako imefungwa. Kwa usaidizi wa jinsi ya kufungua akaunti yako, wasiliana na usaidizi kwa: 0757628885" | ||||
| 
 | ||||
| msgid "Balance: %s\n" | ||||
| msgstr "Salio: %s\n" | ||||
| 
 | ||||
|  | ||||
| @ -28,3 +28,5 @@ flag,flag_gender_set,34,this is set when the gender of the profile is set | ||||
| flag,flag_location_set,35,this is set when the location of the profile is set | ||||
| flag,flag_offerings_set,36,this is set when the offerings of the profile is set | ||||
| flag,flag_back_set,37,this is set when it is a back navigation | ||||
| flag,flag_account_blocked,38,this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded | ||||
| 
 | ||||
|  | ||||
| 
 | 
| @ -1,3 +1,4 @@ | ||||
| CATCH blocked_account flag_account_blocked 1 | ||||
| CATCH select_language flag_language_set 0 | ||||
| CATCH terms flag_account_created 0 | ||||
| LOAD check_account_status 0 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user