Compare commits

..
Author SHA1 Message Date
lash 2587882eae Merge pull request 'pin-reset' (#139) from pin-reset into pre-mock-remove
Reviewed-on: urdt/ussd#139
Reviewed-by: lash <accounts-grassrootseconomics@holbrook.no>
2024-11-02 14:54:19 +01:00
Carlosokumu 6dbe74d12b use single temporary value 2024-11-01 16:46:09 +03:00
Carlosokumu 332074375a wrap in devtools/admin 2024-11-01 16:44:54 +03:00
Carlosokumu 5e4a9e7567 Merge branch 'master' into pin-reset 2024-11-01 16:26:29 +03:00
Carlosokumu eb2c73dce1 remove unused setadmin store 2024-11-01 11:51:50 +03:00
Carlosokumu 7e448f739a change fs store path to root 2024-11-01 09:35:48 +03:00
Carlosokumu 0014693ba8 remove guard pin option 2024-11-01 06:39:37 +03:00
Carlosokumu 2704069e74 Merge branch 'master' into pin-reset 2024-10-31 21:09:48 +03:00
Carlosokumu 7d1a04f089 remove from root 2024-10-31 21:01:24 +03:00
Carlosokumu 53fa6f64ce define structure of json 2024-10-31 21:01:01 +03:00
Carlosokumu 7fa38340dd add command to initialize a list of admin numbers 2024-10-31 21:00:41 +03:00
Carlosokumu 7aab3cff8c remove seed 2024-10-31 21:00:16 +03:00
Carlosokumu 299534ccf1 define seed as a command in the devtool 2024-10-31 20:59:51 +03:00
Carlosokumu b2655b7f11 remove seed from executable 2024-10-31 20:59:11 +03:00
Carlosokumu 5abe9b78cc attach an admin store for the phone numbers 2024-10-31 20:11:26 +03:00
Carlosokumu 12825ae08a setup adminstore in the local handler service 2024-10-31 20:10:46 +03:00
Carlosokumu ac0b4b2ed1 pass context 2024-10-31 20:08:30 +03:00
Carlosokumu 8fe8ff540b Merge branch 'master' into pin-reset 2024-10-31 16:38:28 +03:00
Carlosokumu 767a3cd64c remove pin guard menu option 2024-10-31 09:38:51 +03:00
Carlosokumu c4078c5280 remove extra spaces 2024-10-31 09:21:46 +03:00
Carlosokumu d434194021 catch incorrect pin entry 2024-10-30 23:21:15 +03:00
Carlosokumu c6ca3f6be4 fix sink error 2024-10-30 22:32:38 +03:00
Carlosokumu 8093eae61a use 0 instead of 1 for back 2024-10-30 21:26:52 +03:00
Carlosokumu 833d52a558 remove print message 2024-10-30 21:26:11 +03:00
Carlosokumu 8262e14198 Merge branch 'master' into pin-reset 2024-10-30 21:10:12 +03:00
Carlosokumu ea4c6d9314 check for phone number validity 2024-10-30 18:01:43 +03:00
Carlosokumu 7c823e07ca move to root node after on back 2024-10-30 18:01:20 +03:00
Carlosokumu 41585f831c move catch and load to next node 2024-10-30 18:00:38 +03:00
Carlosokumu d93a26f9b0 remove function exec after HALT 2024-10-30 17:58:52 +03:00
Carlosokumu cf523e30f8 update handler in test 2024-10-30 14:50:31 +03:00
Carlosokumu 888d3befe9 add actual pin reset functionality 2024-10-30 14:50:12 +03:00
Carlosokumu 017691a40c define structure for admin numbers 2024-10-30 14:41:14 +03:00
Carlosokumu dc418771a7 attach an admin store for phone numbers 2024-10-30 14:33:48 +03:00
Carlosokumu c2068db050 update handler functions 2024-10-30 14:33:22 +03:00
Carlosokumu c42b1cd66b provide required handler functions and admin store 2024-10-30 14:32:01 +03:00
Carlosokumu b404ae95fb setup an admin store based on fs 2024-10-30 14:30:38 +03:00
Carlosokumu 0a97f610a4 catch unregistred number 2024-10-29 22:23:22 +03:00
Carlosokumu 5a0563df94 group regex,check for valid number against the regex 2024-10-29 22:17:43 +03:00
Carlosokumu 7597b96dae remove catch for unregistered number 2024-10-29 22:16:34 +03:00
Carlosokumu f37483e2f0 use _ for back navigation 2024-10-29 22:15:31 +03:00
Carlosokumu d0ad6395b5 add check for unregistered phone numbers 2024-10-29 17:35:42 +03:00
Carlosokumu 106983a394 use explicit back to node 2024-10-29 17:35:01 +03:00
Carlosokumu 91b85af11a add reset unregistered number 2024-10-29 17:34:34 +03:00
Carlosokumu 534d756318 catch unregistred phone numbers 2024-10-29 17:18:39 +03:00
Carlosokumu 6998c30dd1 add node to handle unregistered phone numbers 2024-10-29 17:18:01 +03:00
Carlosokumu 449f90c95b add flag to catch unregistred numbers 2024-10-29 17:17:03 +03:00
Carlosokumu e96c874300 repeat same node on invalid option input 2024-10-29 15:02:40 +03:00
Carlosokumu b35460d3c1 Merge branch 'master' into pin-reset 2024-10-29 14:35:17 +03:00
Carlosokumu 124049c924 add admin number defination in env 2024-10-29 14:32:17 +03:00
Carlosokumu 5fd3eb3c29 set admin privilege flag 2024-10-29 14:28:58 +03:00
Carlosokumu d83962c0ba load admin numbers defined in the .env 2024-10-29 14:26:24 +03:00
Carlosokumu 41da099933 remove the admin flag,setup an admin store 2024-10-29 13:24:13 +03:00
Carlosokumu c9bb93ede6 create a simple admin store for phone numbers 2024-10-29 13:15:41 +03:00
Carlosokumu ca13d9155c replace _ with explicit back node 2024-10-29 13:15:22 +03:00
Carlosokumu e338ce0025 load and reload only after input 2024-10-29 13:14:49 +03:00
Carlosokumu b97965193b add pin reset for others handling 2024-10-28 16:37:23 +03:00
Carlosokumu aec0abb2b6 setup an admin flag 2024-10-28 16:34:33 +03:00
Carlosokumu 26073c8000 define handler functions required to reset others pin 2024-10-28 15:49:20 +03:00
Carlosokumu e4c2f644f3 define a key to hold number during pin reset 2024-10-28 15:47:56 +03:00
Carlosokumu 3de46cef5e setup pin reset nodes 2024-10-28 15:45:08 +03:00
Carlosokumu 0cc0bdf9f7 add pin reset for others nodes 2024-10-28 15:19:35 +03:00
Carlosokumu 72d5c186dd add admin privilege flag 2024-10-28 15:18:40 +03:00
40 changed files with 1182 additions and 582 deletions
+4
View File
@@ -16,3 +16,7 @@ CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create
TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/
BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ BALANCE_URL=https://custodial.sarafu.africa/api/account/status/
TRACK_URL=http://localhost:5003/api/v2/account/status TRACK_URL=http://localhost:5003/api/v2/account/status
#numbers with privileges to reset others pin
ADMIN_NUMBERS=254051722XXX,255012221XXX
+1 -1
View File
@@ -131,7 +131,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore) lhs.SetDataStore(&userdataStore)
if err != nil { if err != nil {
+2 -1
View File
@@ -104,8 +104,9 @@ func main() {
os.Exit(1) os.Exit(1)
} }
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore) lhs.SetDataStore(&userdataStore)
accountService := server.AccountService{} accountService := server.AccountService{}
hl, err := lhs.GetHandler(&accountService) hl, err := lhs.GetHandler(&accountService)
+2 -1
View File
@@ -92,13 +92,14 @@ func main() {
os.Exit(1) os.Exit(1)
} }
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore) lhs.SetDataStore(&userdataStore)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, err.Error()) fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1) os.Exit(1)
} }
accountService := server.AccountService{} accountService := server.AccountService{}
hl, err := lhs.GetHandler(&accountService) hl, err := lhs.GetHandler(&accountService)
if err != nil { if err != nil {
+1 -1
View File
@@ -88,7 +88,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdatastore) lhs.SetDataStore(&userdatastore)
lhs.SetPersister(pe) lhs.SetPersister(pe)
+7
View File
@@ -0,0 +1,7 @@
{
"admins": [
{
"phonenumber" : "<replace with any admin number to test with >"
}
]
}
+47
View File
@@ -0,0 +1,47 @@
package commands
import (
"context"
"encoding/json"
"os"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/internal/utils"
)
var (
logg = logging.NewVanilla().WithDomain("adminstore")
)
type Admin struct {
PhoneNumber string `json:"phonenumber"`
}
type Config struct {
Admins []Admin `json:"admins"`
}
func Seed(ctx context.Context) error {
var config Config
adminstore, err := utils.NewAdminStore(ctx, "../admin_numbers")
store := adminstore.FsStore
if err != nil {
return err
}
defer store.Close()
data, err := os.ReadFile("admin_numbers.json")
if err != nil {
return err
}
if err := json.Unmarshal(data, &config); err != nil {
return err
}
for _, admin := range config.Admins {
err := store.Put(ctx, []byte(admin.PhoneNumber), []byte("1"))
if err != nil {
logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber)
return err
}
}
return nil
}
+17
View File
@@ -0,0 +1,17 @@
package main
import (
"context"
"log"
"git.grassecon.net/urdt/ussd/devtools/admin/commands"
)
func main() {
ctx := context.Background()
err := commands.Seed(ctx)
if err != nil {
log.Fatalf("Failed to initialize a list of admins with error %s", err)
}
}
+22 -6
View File
@@ -1,6 +1,8 @@
package handlers package handlers
import ( import (
"context"
"git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/asm"
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/engine"
@@ -8,6 +10,7 @@ import (
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/server"
"git.grassecon.net/urdt/ussd/internal/handlers/ussd" "git.grassecon.net/urdt/ussd/internal/handlers/ussd"
"git.grassecon.net/urdt/ussd/internal/utils"
) )
type HandlerService interface { type HandlerService interface {
@@ -28,20 +31,26 @@ type LocalHandlerService struct {
DbRs *resource.DbResource DbRs *resource.DbResource
Pe *persist.Persister Pe *persist.Persister
UserdataStore *db.Db UserdataStore *db.Db
AdminStore *utils.AdminStore
Cfg engine.Config Cfg engine.Config
Rs resource.Resource Rs resource.Resource
} }
func NewLocalHandlerService(fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) { func NewLocalHandlerService(ctx context.Context, fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) {
parser, err := getParser(fp, debug) parser, err := getParser(fp, debug)
if err != nil { if err != nil {
return nil, err return nil, err
} }
adminstore, err := utils.NewAdminStore(ctx, "admin_numbers")
if err != nil {
return nil, err
}
return &LocalHandlerService{ return &LocalHandlerService{
Parser: parser, Parser: parser,
DbRs: dbResource, DbRs: dbResource,
Cfg: cfg, AdminStore: adminstore,
Rs: rs, Cfg: cfg,
Rs: rs,
}, nil }, nil
} }
@@ -54,7 +63,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
} }
func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) {
ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -98,6 +107,13 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList)
ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher)
ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher) ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher)
ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin)
ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch)
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)
return ussdHandlers, nil return ussdHandlers, nil
} }
+161 -11
View File
@@ -35,6 +35,12 @@ var (
errResponse *api.ErrResponse errResponse *api.ErrResponse
) )
// Define the regex patterns as constants
const (
phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}`
pinPattern = `^\d{4}$`
)
// FlagManager handles centralized flag management // FlagManager handles centralized flag management
type FlagManager struct { type FlagManager struct {
parser *asm.FlagParser parser *asm.FlagParser
@@ -63,12 +69,13 @@ type Handlers struct {
st *state.State st *state.State
ca cache.Memory ca cache.Memory
userdataStore utils.DataStore userdataStore utils.DataStore
adminstore *utils.AdminStore
flagManager *asm.FlagParser flagManager *asm.FlagParser
accountService server.AccountServiceInterface accountService server.AccountServiceInterface
prefixDb storage.PrefixDb prefixDb storage.PrefixDb
} }
func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) { func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService server.AccountServiceInterface) (*Handlers, error) {
if userdataStore == nil { if userdataStore == nil {
return nil, fmt.Errorf("cannot create handler with nil userdata store") return nil, fmt.Errorf("cannot create handler with nil userdata store")
} }
@@ -81,21 +88,24 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s
h := &Handlers{ h := &Handlers{
userdataStore: userDb, userdataStore: userDb,
flagManager: appFlags, flagManager: appFlags,
adminstore: adminstore,
accountService: accountService, accountService: accountService,
prefixDb: prefixDb, prefixDb: prefixDb,
} }
return h, nil return h, nil
} }
// Define the regex pattern as a constant
const pinPattern = `^\d{4}$`
// isValidPIN checks whether the given input is a 4 digit number // isValidPIN checks whether the given input is a 4 digit number
func isValidPIN(pin string) bool { func isValidPIN(pin string) bool {
match, _ := regexp.MatchString(pinPattern, pin) match, _ := regexp.MatchString(pinPattern, pin)
return match return match
} }
func isValidPhoneNumber(phonenumber string) bool {
match, _ := regexp.MatchString(phoneRegex, phonenumber)
return match
}
func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers {
if h.pe != nil { if h.pe != nil {
panic("persister already set") panic("persister already set")
@@ -106,13 +116,25 @@ func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers {
func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var r resource.Result var r resource.Result
if h.pe == nil { if h.pe == nil {
logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca) logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca)
return r, nil return r, nil
} }
h.st = h.pe.GetState() h.st = h.pe.GetState()
h.ca = h.pe.GetMemory() h.ca = h.pe.GetMemory()
sessionId, _ := ctx.Value("SessionId").(string)
flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege")
isAdmin, _ := h.adminstore.IsAdmin(sessionId)
if isAdmin {
r.FlagSet = append(r.FlagSet, flag_admin_privilege)
} else {
r.FlagReset = append(r.FlagReset, flag_admin_privilege)
}
if h.st == nil || h.ca == nil { if h.st == nil || h.ca == nil {
logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca) logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca)
return r, fmt.Errorf("cannot get state and memory for handler") return r, fmt.Errorf("cannot get state and memory for handler")
@@ -203,6 +225,30 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte)
return res, nil return res, nil
} }
func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byte) (resource.Result, error) {
res := resource.Result{}
flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch")
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
store := h.userdataStore
blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
if err != nil {
return res, err
}
temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE)
if err != nil {
return res, err
}
if bytes.Equal(temporaryPin, input) {
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
} else {
res.FlagSet = append(res.FlagSet, flag_pin_mismatch)
}
return res, nil
}
func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
res := resource.Result{} res := resource.Result{}
_, ok := ctx.Value("SessionId").(string) _, ok := ctx.Value("SessionId").(string)
@@ -234,7 +280,6 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
} }
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
accountPIN := string(input) accountPIN := string(input)
// Validate that the PIN is a 4-digit number // Validate that the PIN is a 4-digit number
@@ -242,9 +287,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
res.FlagSet = append(res.FlagSet, flag_incorrect_pin) res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
return res, nil return res, nil
} }
res.FlagReset = append(res.FlagReset, flag_incorrect_pin) res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
store := h.userdataStore store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN)) err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN))
if err != nil { if err != nil {
@@ -254,6 +297,29 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
return res, nil return res, nil
} }
func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
temporaryPin := string(input)
blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
if err != nil {
return res, err
}
err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE, []byte(temporaryPin))
if err != nil {
return res, err
}
return res, nil
}
func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
@@ -298,7 +364,6 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte
if err != nil { if err != nil {
return res, err return res, err
} }
if bytes.Equal(input, temporaryPin) { if bytes.Equal(input, temporaryPin) {
res.FlagSet = []uint32{flag_valid_pin} res.FlagSet = []uint32{flag_valid_pin}
res.FlagReset = []uint32{flag_pin_mismatch} res.FlagReset = []uint32{flag_pin_mismatch}
@@ -511,6 +576,14 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt
return res, nil return res, nil
} }
// ResetAllowUpdate resets the allowupdate flag that allows a user to update profile data.
func (h *Handlers) ResetValidPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin")
res.FlagReset = append(res.FlagReset, flag_valid_pin)
return res, nil
}
// ResetAccountAuthorized resets the account authorization flag after a successful PIN entry. // ResetAccountAuthorized resets the account authorization flag after a successful PIN entry.
func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
@@ -594,11 +667,13 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
store := h.userdataStore store := h.userdataStore
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
if err != nil { if err != nil {
return res, err return res, err
} }
okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error) res.FlagSet = append(res.FlagSet, flag_api_error)
@@ -655,7 +730,6 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res
var err error var err error
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
date := string(input) date := string(input)
_, err = strconv.Atoi(date) _, err = strconv.Atoi(date)
if err != nil { if err != nil {
@@ -678,7 +752,6 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by
var res resource.Result var res resource.Result
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) res.FlagReset = append(res.FlagReset, flag_incorrect_date_format)
return res, nil return res, nil
} }
@@ -761,6 +834,67 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input
return res, nil return res, nil
} }
func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
if err != nil {
return res, err
}
temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_VALUE)
if err != nil {
return res, err
}
err = store.WriteEntry(ctx, string(blockedPhonenumber), utils.DATA_ACCOUNT_PIN, []byte(temporaryPin))
if err != nil {
return res, nil
}
return res, nil
}
func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number")
res.FlagReset = append(res.FlagReset, flag_unregistered_number)
return res, nil
}
func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number")
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
blockedNumber := string(input)
_, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY)
if !isValidPhoneNumber(blockedNumber) {
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
return res, nil
}
if err != nil {
if db.IsNotFound(err) {
logg.Printf(logging.LVL_INFO, "Invalid or unregistered number")
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
return res, nil
} else {
return res, err
}
}
err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber))
if err != nil {
return res, nil
}
return res, nil
}
// ValidateRecipient validates that the given input is a valid phone number. // ValidateRecipient validates that the given input is a valid phone number.
func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
@@ -932,6 +1066,22 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (
return res, nil return res, nil
} }
// RetrieveBlockedNumber gets the current number during the pin reset for other's is in progress.
func (h *Handlers) RetrieveBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
store := h.userdataStore
blockedNumber, _ := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
res.Content = string(blockedNumber)
return res, nil
}
// GetSender returns the sessionId (phoneNumber) // GetSender returns the sessionId (phoneNumber)
func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -73,7 +73,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
os.Exit(1) os.Exit(1)
} }
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userDataStore) lhs.SetDataStore(&userDataStore)
lhs.SetPersister(pe) lhs.SetPersister(pe)
+59
View File
@@ -0,0 +1,59 @@
package mocks
import (
"context"
"git.defalsify.org/vise.git/lang"
"github.com/stretchr/testify/mock"
)
type MockDb struct {
mock.Mock
}
func (m *MockDb) SetPrefix(prefix uint8) {
m.Called(prefix)
}
func (m *MockDb) Prefix() uint8 {
args := m.Called()
return args.Get(0).(uint8)
}
func (m *MockDb) Safe() bool {
args := m.Called()
return args.Get(0).(bool)
}
func (m *MockDb) SetLanguage(language *lang.Language) {
m.Called(language)
}
func (m *MockDb) SetLock(uint8, bool) error {
args := m.Called()
return args.Error(0)
}
func (m *MockDb) Connect(ctx context.Context, connectionStr string) error {
args := m.Called(ctx, connectionStr)
return args.Error(0)
}
func (m *MockDb) SetSession(sessionId string) {
m.Called(sessionId)
}
func (m *MockDb) Put(ctx context.Context, key, value []byte) error {
args := m.Called(ctx, key, value)
return args.Error(0)
}
func (m *MockDb) Get(ctx context.Context, key []byte) ([]byte, error) {
args := m.Called(ctx, key)
return nil, args.Error(0)
}
func (m *MockDb) Close() error {
args := m.Called(nil)
return args.Error(0)
}
@@ -0,0 +1,21 @@
package mocks
import (
"context"
"github.com/stretchr/testify/mock"
)
type MockSubPrefixDb struct {
mock.Mock
}
func (m *MockSubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) {
args := m.Called(ctx, key)
return args.Get(0).([]byte), args.Error(1)
}
func (m *MockSubPrefixDb) Put(ctx context.Context, key, val []byte) error {
args := m.Called(ctx, key, val)
return args.Error(0)
}
+24
View File
@@ -0,0 +1,24 @@
package mocks
import (
"context"
"git.defalsify.org/vise.git/db"
"git.grassecon.net/urdt/ussd/internal/utils"
"github.com/stretchr/testify/mock"
)
type MockUserDataStore struct {
db.Db
mock.Mock
}
func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) {
args := m.Called(ctx, sessionId, typ)
return args.Get(0).([]byte), args.Error(1)
}
func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ utils.DataTyp, value []byte) error {
args := m.Called(ctx, sessionId, typ, value)
return args.Error(0)
}
+51
View File
@@ -0,0 +1,51 @@
package utils
import (
"context"
"git.defalsify.org/vise.git/db"
fsdb "git.defalsify.org/vise.git/db/fs"
"git.defalsify.org/vise.git/logging"
)
var (
logg = logging.NewVanilla().WithDomain("adminstore")
)
type AdminStore struct {
ctx context.Context
FsStore db.Db
}
func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) {
fsStore, err := getFsStore(ctx, fileName)
if err != nil {
return nil, err
}
return &AdminStore{ctx: ctx, FsStore: fsStore}, nil
}
func getFsStore(ctx context.Context, connectStr string) (db.Db, error) {
fsStore := fsdb.NewFsDb()
err := fsStore.Connect(ctx, connectStr)
fsStore.SetPrefix(db.DATATYPE_USERDATA)
if err != nil {
return nil, err
}
return fsStore, nil
}
// Checks if the given sessionId is listed as an admin.
func (as *AdminStore) IsAdmin(sessionId string) (bool, error) {
_, err := as.FsStore.Get(as.ctx, []byte(sessionId))
if err != nil {
if db.IsNotFound(err) {
logg.Printf(logging.LVL_INFO, "Returning false because session id was not found")
return false, nil
} else {
return false, err
}
}
return true, nil
}
+1
View File
@@ -26,6 +26,7 @@ const (
DATA_VOUCHER_LIST DATA_VOUCHER_LIST
DATA_ACTIVE_SYM DATA_ACTIVE_SYM
DATA_ACTIVE_BAL DATA_ACTIVE_BAL
DATA_BLOCKED_NUMBER
DATA_PUBLIC_KEY_REVERSE DATA_PUBLIC_KEY_REVERSE
DATA_ACTIVE_DECIMAL DATA_ACTIVE_DECIMAL
DATA_ACTIVE_ADDRESS DATA_ACTIVE_ADDRESS
+1 -1
View File
@@ -13,7 +13,7 @@
}, },
{ {
"input": "5", "input": "5",
"expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n3:Guard my PIN\n0:Back" "expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@@ -0,0 +1 @@
Please confirm new PIN for:{{.retrieve_blocked_number}}
@@ -0,0 +1,14 @@
CATCH pin_entry flag_incorrect_pin 1
RELOAD retrieve_blocked_number
MAP retrieve_blocked_number
CATCH invalid_others_pin flag_valid_pin 0
CATCH pin_reset_result flag_account_authorized 1
LOAD save_others_temporary_pin 6
RELOAD save_others_temporary_pin
MOUT back 0
HALT
INCMP _ 0
LOAD check_pin_mismatch 0
RELOAD check_pin_mismatch
CATCH others_pin_mismatch flag_pin_mismatch 1
INCMP pin_entry *
@@ -3,5 +3,3 @@ MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
INCMP * pin_reset_success INCMP * pin_reset_success
+1
View File
@@ -0,0 +1 @@
Enter other's phone number:
@@ -0,0 +1,7 @@
CATCH no_admin_privilege flag_admin_privilege 0
LOAD reset_account_authorized 0
RELOAD reset_account_authorized
MOUT back 0
HALT
INCMP _ 0
INCMP enter_others_new_pin *
@@ -0,0 +1 @@
Please enter new PIN for: {{.retrieve_blocked_number}}
@@ -0,0 +1,12 @@
LOAD validate_blocked_number 6
RELOAD validate_blocked_number
CATCH unregistered_number flag_unregistered_number 1
LOAD retrieve_blocked_number 0
RELOAD retrieve_blocked_number
MAP retrieve_blocked_number
MOUT back 0
HALT
LOAD verify_new_pin 6
RELOAD verify_new_pin
INCMP _ 0
INCMP * confirm_others_new_pin
-1
View File
@@ -1 +0,0 @@
Guard my PIN
-1
View File
@@ -1 +0,0 @@
Linda PIN yangu
+1
View File
@@ -0,0 +1 @@
The PIN you have entered is invalid.Please try a 4 digit number instead.
@@ -0,0 +1,5 @@
MOUT retry 1
MOUT quit 9
HALT
INCMP enter_others_new_pin 1
INCMP quit 9
+1
View File
@@ -0,0 +1 @@
You do not have privileges to perform this action
@@ -0,0 +1,5 @@
MOUT quit 9
MOUT back 0
HALT
INCMP pin_management 0
INCMP quit 9
@@ -0,0 +1 @@
The PIN you have entered is not a match
@@ -0,0 +1,5 @@
MOUT retry 1
MOUT quit 9
HALT
INCMP _ 1
INCMP quit 9
+3 -3
View File
@@ -1,8 +1,8 @@
MOUT change_pin 1 MOUT change_pin 1
MOUT reset_pin 2 MOUT reset_pin 2
MOUT guard_pin 3
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP my_account 0
INCMP old_pin 1 INCMP old_pin 1
INCMP enter_other_number 2
INCMP . *
+1
View File
@@ -0,0 +1 @@
PIN reset request for {{.retrieve_blocked_number}} was successful
@@ -0,0 +1,8 @@
LOAD retrieve_blocked_number 0
MAP retrieve_blocked_number
LOAD reset_others_pin 6
MOUT back 0
MOUT quit 9
HALT
INCMP pin_management 0
INCMP quit 9
@@ -6,5 +6,3 @@ MOUT quit 9
HALT HALT
INCMP main 0 INCMP main 0
INCMP quit 9 INCMP quit 9
+2
View File
@@ -17,3 +17,5 @@ flag,flag_incorrect_date_format,23,this is set when the given year of birth is i
flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid
flag,flag_api_call_error,25,this is set when communication to an external service fails flag,flag_api_call_error,25,this is set when communication to an external service fails
flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher
flag,flag_admin_privilege,27,this is set when a user has admin privileges.
flag,flag_unregistered_number,28,this is set when an unregistered phonenumber tries to perform an action
1 flag flag_language_set 8 checks whether the user has set their prefered language
17 flag flag_incorrect_voucher 24 this is set when the selected voucher is invalid
18 flag flag_api_call_error 25 this is set when communication to an external service fails
19 flag flag_no_active_voucher 26 this is set when a user does not have an active voucher
20 flag flag_admin_privilege 27 this is set when a user has admin privileges.
21 flag flag_unregistered_number 28 this is set when an unregistered phonenumber tries to perform an action
@@ -0,0 +1 @@
The number you have entered is either not registered with Sarafu or is invalid.
@@ -0,0 +1,7 @@
LOAD reset_unregistered_number 0
RELOAD reset_unregistered_number
MOUT back 0
MOUT quit 9
HALT
INCMP ^ 0
INCMP quit 9