Compare commits

...

37 Commits

Author SHA1 Message Date
lash
bcf1965a6c Merge branch 'master' into lash/purify-max 2025-01-09 13:48:39 +00:00
1f0568df32 Merge pull request 'Implement connstring handling' (#247) from lash/purify-more into master
Reviewed-on: urdt/ussd#247
2025-01-09 13:03:28 +01:00
lash
24c513d4f0 Merge branch 'master' into lash/purify-more 2025-01-09 12:02:17 +00:00
b3fd6f5c1a Merge pull request 'Rename handler/ussd package' (#254) from konstantinmds/ussd:refactor/24-rename-ussd-to-application into master
Reviewed-on: urdt/ussd#254
2025-01-09 13:01:19 +01:00
5734011f96 refactor: rename ussd package to application (#24)
- Rename internal/handlers/ussd directory to application
- Update all imports and references to use new package name
2025-01-08 13:40:00 +01:00
lash
379d98ccd5 Merge branch 'master' into lash/purify-more 2025-01-08 12:32:11 +00:00
f40e11c267 Merge pull request 'account-pin-block-v2' (#256) from account-pin-block-v2 into master
Reviewed-on: urdt/ussd#256
2025-01-08 13:30:39 +01:00
b698f08136
chore: add space after punctuation 2025-01-08 15:27:10 +03:00
4d7589ad95 Merge branch 'master' into lash/purify-more 2025-01-08 13:07:50 +01:00
efdb52bccd
chore: add space after punctuation 2025-01-08 14:54:58 +03:00
2ff9fed3c5
chore: rename countIncorrectPINAttempts to incrementIncorrectPINAttempts 2025-01-08 14:54:57 +03:00
477b4cf8f6
chore : rename remainingPINAttempts to currentWrongPinAttemptsCount 2025-01-08 14:54:57 +03:00
ed6651697a
chore : add variable description to AllowedPINAttempts 2025-01-08 14:54:56 +03:00
c359d99075 Merge branch 'master' into account-pin-block-v2 2025-01-08 10:00:46 +01:00
8d477356f3
update tests 2025-01-08 11:47:55 +03:00
7f3294a8a2
update tests 2025-01-08 11:47:41 +03:00
lash
721f80d0f2
Repalce missing context 2025-01-08 07:34:22 +00:00
f49e54a562 Merge pull request 'Space after comma' (#259) from lash/helpcomma into master
Reviewed-on: urdt/ussd#259
2025-01-08 07:57:18 +01:00
lash
5081b6d4ce
Space after comma 2025-01-08 06:48:35 +00:00
4d72ae0313
add handler showing a message for a blocked account 2025-01-08 09:30:51 +03:00
4fe64a7747
show message for a blocked account 2025-01-08 09:29:00 +03:00
3004698d5b
add a message for a blocked account 2025-01-08 09:28:31 +03:00
50c7ff1046
register handler to show blocked account message 2025-01-08 09:27:45 +03:00
07b061a68b
remove blocked account templates 2025-01-08 09:26:53 +03:00
6339f0c2e5 Merge branch 'master' into lash/purify-more 2025-01-07 17:58:19 +01:00
lash
1fa830f286
Add auth conn string to ssh, use connstr for execs 2025-01-07 13:51:26 +00:00
64fba91670
catch blocked account 2025-01-07 14:38:44 +03:00
c15958a1ad
reset incorrect pin attempts on correct entry 2025-01-07 14:32:44 +03:00
ee442daefa
add blocked account node 2025-01-07 14:03:53 +03:00
656052dc74 Merge pull request 'trim any leading whitespace in the input' (#258) from send-input-fix into master
Some checks failed
release / docker (push) Has been cancelled
Reviewed-on: urdt/ussd#258
Reviewed-by: lash <accounts-grassrootseconomics@holbrook.no>
2025-01-07 10:33:20 +01:00
6c5873da6f
trim any leading whitespace in the input 2025-01-07 12:15:15 +03:00
11d30583a4
map content of reset_incorrect and catch blocked account 2025-01-07 10:50:30 +03:00
f83f539046
add node to show remaining pin attempts 2025-01-07 10:48:59 +03:00
562bd4fa24
check for incorrect pin 2025-01-06 22:54:31 +03:00
90df0eefc3
add value for allowed number of PIN attempts 2025-01-06 22:53:59 +03:00
b37f2a0a11
add flag for when an account has been blocked 2025-01-06 21:06:54 +03:00
68e4c9af03
add key for incorrect pin attempts 2025-01-06 21:00:34 +03:00
19 changed files with 324 additions and 108 deletions

View File

@ -75,7 +75,7 @@ func main() {
if connStr != "" {
connStr = config.DbConn
}
connData, err := storage.ToConnData(config.DbConn)
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)

View File

@ -62,7 +62,7 @@ func main() {
if connStr != "" {
connStr = config.DbConn
}
connData, err := storage.ToConnData(config.DbConn)
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)

View File

@ -55,7 +55,7 @@ func main() {
if connStr != "" {
connStr = config.DbConn
}
connData, err := storage.ToConnData(config.DbConn)
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)

View File

@ -37,7 +37,7 @@ func main() {
config.LoadConfig()
var connStr string
var dbDir string
var authConnStr string
var resourceDir string
var size uint
var engineDebug bool
@ -45,6 +45,7 @@ func main() {
var host string
var port uint
flag.StringVar(&connStr, "c", "", "connection string")
flag.StringVar(&authConnStr, "authdb", "", "auth connection string")
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output")
@ -52,14 +53,22 @@ func main() {
flag.UintVar(&port, "p", 7122, "socket port")
flag.Parse()
if connStr != "" {
if connStr == "" {
connStr = config.DbConn
}
connData, err := storage.ToConnData(config.DbConn)
if authConnStr == "" {
authConnStr = connStr
}
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)
}
authConnData, err := storage.ToConnData(authConnStr)
if err != nil {
fmt.Fprintf(os.Stderr, "auth connstr err: %v", err)
os.Exit(1)
}
sshKeyFile := flag.Arg(0)
_, err = os.Stat(sshKeyFile)
@ -75,7 +84,7 @@ func main() {
logg.WarnCtxf(ctx, "!!!!! Do not expose to internet and only use with tunnel!")
logg.WarnCtxf(ctx, "!!!!! (See ssh -L <...>)")
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "keyfile", sshKeyFile, "host", host, "port", port)
logg.Infof("start command", "conn", connData, "authconn", authConnData, "resourcedir", resourceDir, "outputsize", size, "keyfile", sshKeyFile, "host", host, "port", port)
pfp := path.Join(scriptDir, "pp.csv")
@ -91,7 +100,7 @@ func main() {
cfg.EngineDebug = true
}
authKeyStore, err := ssh.NewSshKeyStore(ctx, dbDir)
authKeyStore, err := ssh.NewSshKeyStore(ctx, authConnData.String())
if err != nil {
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
os.Exit(1)

View File

@ -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 (

View File

@ -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

View File

@ -9,7 +9,7 @@ import (
"git.grassecon.net/urdt/ussd/request"
"git.grassecon.net/urdt/ussd/errors"
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
"git.grassecon.net/urdt/ussd/internal/handlers/application"
"git.grassecon.net/urdt/ussd/internal/storage"
)
@ -21,28 +21,28 @@ type BaseSessionHandler struct {
cfgTemplate engine.Config
rp request.RequestParser
rs resource.Resource
hn *ussd.Handlers
hn *application.Handlers
provider storage.StorageProvider
}
func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp request.RequestParser, hn *ussd.Handlers) *BaseSessionHandler {
func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp request.RequestParser, hn *application.Handlers) *BaseSessionHandler {
return &BaseSessionHandler{
cfgTemplate: cfg,
rs: rs,
hn: hn,
rp: rp,
provider: storage.NewSimpleStorageProvider(stateDb, userdataDb),
rs: rs,
hn: hn,
rp: rp,
provider: storage.NewSimpleStorageProvider(stateDb, userdataDb),
}
}
func(f* BaseSessionHandler) Shutdown() {
func (f *BaseSessionHandler) Shutdown() {
err := f.provider.Close()
if err != nil {
logg.Errorf("handler shutdown error", "err", err)
}
}
func(f *BaseSessionHandler) GetEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) engine.Engine {
func (f *BaseSessionHandler) GetEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) engine.Engine {
en := engine.NewEngine(cfg, rs)
en = en.WithPersister(pr)
return en
@ -52,7 +52,7 @@ func(f *BaseSessionHandler) Process(rqs request.RequestSession) (request.Request
var r bool
var err error
var ok bool
logg.InfoCtxf(rqs.Ctx, "new request", "data", rqs)
rqs.Storage, err = f.provider.Get(rqs.Config.SessionId)
@ -91,7 +91,7 @@ func(f *BaseSessionHandler) Process(rqs request.RequestSession) (request.Request
return rqs, err
}
rqs.Continue = r
rqs.Continue = r
return rqs, nil
}
@ -106,10 +106,14 @@ func(f *BaseSessionHandler) Reset(rqs request.RequestSession) (request.RequestSe
return rqs, rqs.Engine.Finish()
}
func(f *BaseSessionHandler) GetConfig() engine.Config {
func (f *BaseSessionHandler) GetConfig() engine.Config {
return f.cfgTemplate
}
<<<<<<< HEAD:handlers/base.go
func(f *BaseSessionHandler) GetRequestParser() request.RequestParser {
=======
func (f *BaseSessionHandler) GetRequestParser() RequestParser {
>>>>>>> master:internal/handlers/base.go
return f.rp
}

View File

@ -10,13 +10,13 @@ import (
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
"git.grassecon.net/urdt/ussd/internal/handlers/application"
"git.grassecon.net/urdt/ussd/internal/utils"
"git.grassecon.net/urdt/ussd/remote"
)
type HandlerService interface {
GetHandler() (*ussd.Handlers, error)
GetHandler() (*application.Handlers, error)
}
func getParser(fp string, debug bool) (*asm.FlagParser, error) {
@ -64,72 +64,73 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
ls.UserdataStore = db
}
func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) {
func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*application.Handlers, error) {
replaceSeparatorFunc := func(input string) string {
return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator)
}
ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparatorFunc)
appHandlers, err := application.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparatorFunc)
if err != nil {
return nil, err
}
ussdHandlers = ussdHandlers.WithPersister(ls.Pe)
ls.DbRs.AddLocalFunc("set_language", ussdHandlers.SetLanguage)
ls.DbRs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
ls.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
ls.DbRs.AddLocalFunc("verify_create_pin", ussdHandlers.VerifyCreatePin)
ls.DbRs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
ls.DbRs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
ls.DbRs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
ls.DbRs.AddLocalFunc("quit", ussdHandlers.Quit)
ls.DbRs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
ls.DbRs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
ls.DbRs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
ls.DbRs.AddLocalFunc("invite_valid_recipient", ussdHandlers.InviteValidRecipient)
ls.DbRs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
ls.DbRs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
ls.DbRs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
ls.DbRs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
ls.DbRs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
ls.DbRs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
ls.DbRs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
ls.DbRs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
ls.DbRs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
ls.DbRs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
ls.DbRs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
ls.DbRs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
ls.DbRs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
ls.DbRs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
ls.DbRs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
ls.DbRs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp)
ls.DbRs.AddLocalFunc("fetch_community_balance", ussdHandlers.FetchCommunityBalance)
ls.DbRs.AddLocalFunc("set_default_voucher", ussdHandlers.SetDefaultVoucher)
ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers)
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList)
ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher)
ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher)
ls.DbRs.AddLocalFunc("get_voucher_details", ussdHandlers.GetVoucherDetails)
ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin)
ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckBlockedNumPinMisMatch)
ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber)
ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber)
ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber)
ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin)
ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin)
ls.DbRs.AddLocalFunc("get_current_profile_info", ussdHandlers.GetCurrentProfileInfo)
ls.DbRs.AddLocalFunc("check_transactions", ussdHandlers.CheckTransactions)
ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactionsList)
ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement)
ls.DbRs.AddLocalFunc("update_all_profile_items", ussdHandlers.UpdateAllProfileItems)
ls.DbRs.AddLocalFunc("set_back", ussdHandlers.SetBack)
appHandlers = appHandlers.WithPersister(ls.Pe)
ls.DbRs.AddLocalFunc("set_language", appHandlers.SetLanguage)
ls.DbRs.AddLocalFunc("create_account", appHandlers.CreateAccount)
ls.DbRs.AddLocalFunc("save_temporary_pin", appHandlers.SaveTemporaryPin)
ls.DbRs.AddLocalFunc("verify_create_pin", appHandlers.VerifyCreatePin)
ls.DbRs.AddLocalFunc("check_identifier", appHandlers.CheckIdentifier)
ls.DbRs.AddLocalFunc("check_account_status", appHandlers.CheckAccountStatus)
ls.DbRs.AddLocalFunc("authorize_account", appHandlers.Authorize)
ls.DbRs.AddLocalFunc("quit", appHandlers.Quit)
ls.DbRs.AddLocalFunc("check_balance", appHandlers.CheckBalance)
ls.DbRs.AddLocalFunc("validate_recipient", appHandlers.ValidateRecipient)
ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset)
ls.DbRs.AddLocalFunc("invite_valid_recipient", appHandlers.InviteValidRecipient)
ls.DbRs.AddLocalFunc("max_amount", appHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("validate_amount", appHandlers.ValidateAmount)
ls.DbRs.AddLocalFunc("reset_transaction_amount", appHandlers.ResetTransactionAmount)
ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient)
ls.DbRs.AddLocalFunc("get_sender", appHandlers.GetSender)
ls.DbRs.AddLocalFunc("get_amount", appHandlers.GetAmount)
ls.DbRs.AddLocalFunc("reset_incorrect", appHandlers.ResetIncorrectPin)
ls.DbRs.AddLocalFunc("save_firstname", appHandlers.SaveFirstname)
ls.DbRs.AddLocalFunc("save_familyname", appHandlers.SaveFamilyname)
ls.DbRs.AddLocalFunc("save_gender", appHandlers.SaveGender)
ls.DbRs.AddLocalFunc("save_location", appHandlers.SaveLocation)
ls.DbRs.AddLocalFunc("save_yob", appHandlers.SaveYob)
ls.DbRs.AddLocalFunc("save_offerings", appHandlers.SaveOfferings)
ls.DbRs.AddLocalFunc("reset_account_authorized", appHandlers.ResetAccountAuthorized)
ls.DbRs.AddLocalFunc("reset_allow_update", appHandlers.ResetAllowUpdate)
ls.DbRs.AddLocalFunc("get_profile_info", appHandlers.GetProfileInfo)
ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob)
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob)
ls.DbRs.AddLocalFunc("initiate_transaction", appHandlers.InitiateTransaction)
ls.DbRs.AddLocalFunc("verify_new_pin", appHandlers.VerifyNewPin)
ls.DbRs.AddLocalFunc("confirm_pin_change", appHandlers.ConfirmPinChange)
ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp)
ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance)
ls.DbRs.AddLocalFunc("set_default_voucher", appHandlers.SetDefaultVoucher)
ls.DbRs.AddLocalFunc("check_vouchers", appHandlers.CheckVouchers)
ls.DbRs.AddLocalFunc("get_vouchers", appHandlers.GetVoucherList)
ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher)
ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher)
ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails)
ls.DbRs.AddLocalFunc("reset_valid_pin", appHandlers.ResetValidPin)
ls.DbRs.AddLocalFunc("check_pin_mismatch", appHandlers.CheckBlockedNumPinMisMatch)
ls.DbRs.AddLocalFunc("validate_blocked_number", appHandlers.ValidateBlockedNumber)
ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber)
ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber)
ls.DbRs.AddLocalFunc("reset_others_pin", appHandlers.ResetOthersPin)
ls.DbRs.AddLocalFunc("save_others_temporary_pin", appHandlers.SaveOthersTemporaryPin)
ls.DbRs.AddLocalFunc("get_current_profile_info", appHandlers.GetCurrentProfileInfo)
ls.DbRs.AddLocalFunc("check_transactions", appHandlers.CheckTransactions)
ls.DbRs.AddLocalFunc("get_transactions", appHandlers.GetTransactionsList)
ls.DbRs.AddLocalFunc("view_statement", appHandlers.ViewTransactionStatement)
ls.DbRs.AddLocalFunc("update_all_profile_items", appHandlers.UpdateAllProfileItems)
ls.DbRs.AddLocalFunc("set_back", appHandlers.SetBack)
ls.DbRs.AddLocalFunc("show_blocked_account", appHandlers.ShowBlockedAccount)
return ussdHandlers, nil
return appHandlers, nil
}
// TODO: enable setting of sessionId on engine init time

View File

@ -1,4 +1,4 @@
package ussd
package application
import (
"bytes"
@ -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
}
@ -835,11 +873,21 @@ func (h *Handlers) QuitWithHelp(ctx context.Context, sym string, input []byte) (
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
res.Content = l.Get("For more help,please call: 0757628885")
res.Content = l.Get("For more help, please call: 0757628885")
res.FlagReset = append(res.FlagReset, flag_account_authorized)
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
}

View File

@ -1,10 +1,11 @@
package ussd
package application
import (
"context"
"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))
}

View File

@ -41,6 +41,7 @@ func NewAuther(ctx context.Context, keyStore *SshKeyStore) *auther {
}
func(a *auther) Check(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
logg.TraceCtxf(a.Ctx, "looking for publickey", "pubkey", fmt.Sprintf("%x", pubKey))
va, err := a.keyStore.Get(a.Ctx, pubKey)
if err != nil {
return nil, err
@ -203,6 +204,7 @@ func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
// adapted example from crypto/ssh package, NewServerConn doc
func(s *SshRunner) Run(ctx context.Context, keyStore *SshKeyStore) {
s.Ctx = ctx
running := true
// TODO: waitgroup should probably not be global

View File

@ -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 @@
]
}
]
}
}

View File

@ -0,0 +1,2 @@
LOAD show_blocked_account 0
HALT

View File

@ -1 +1 @@
Incorrect PIN
Incorrect PIN. You have: {{.reset_incorrect}} remaining attempt(s).

View File

@ -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

View File

@ -1 +1 @@
PIN ulioeka sio sahihi
PIN ulioeka sio sahihi, una majaribio: {{.reset_incorrect}} yaliyobaki

View File

@ -7,8 +7,11 @@ msgstr "Ombi lako limetumwa. %s atapokea %s %s kutoka kwa %s."
msgid "Thank you for using Sarafu. Goodbye!"
msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!"
msgid "For more help,please call: 0757628885"
msgstr "Kwa usaidizi zaidi,piga: 0757628885"
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"

View File

@ -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 flag flag_language_set 8 checks whether the user has set their prefered language
28 flag flag_location_set 35 this is set when the location of the profile is set
29 flag flag_offerings_set 36 this is set when the offerings of the profile is set
30 flag flag_back_set 37 this is set when it is a back navigation
31 flag flag_account_blocked 38 this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded
32

View File

@ -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