forked from urdt/ussd
Merge branch 'pre-mock-remove' into lash/export-to-term
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.defalsify.org/vise.git/asm"
|
||||
"git.defalsify.org/vise.git/db"
|
||||
"git.defalsify.org/vise.git/engine"
|
||||
@@ -29,20 +31,26 @@ type LocalHandlerService struct {
|
||||
DbRs *resource.DbResource
|
||||
Pe *persist.Persister
|
||||
UserdataStore *db.Db
|
||||
AdminStore *utils.AdminStore
|
||||
Cfg engine.Config
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adminstore, err := utils.NewAdminStore(ctx, "admin_numbers")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LocalHandlerService{
|
||||
Parser: parser,
|
||||
DbRs: dbResource,
|
||||
Cfg: cfg,
|
||||
Rs: rs,
|
||||
Parser: parser,
|
||||
DbRs: dbResource,
|
||||
AdminStore: adminstore,
|
||||
Cfg: cfg,
|
||||
Rs: rs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -55,7 +63,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
|
||||
}
|
||||
|
||||
func (ls *LocalHandlerService) GetHandler(accountService remote.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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -99,6 +107,13 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn
|
||||
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList)
|
||||
ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ var (
|
||||
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
|
||||
type FlagManager struct {
|
||||
parser *asm.FlagParser
|
||||
@@ -63,12 +69,13 @@ type Handlers struct {
|
||||
st *state.State
|
||||
ca cache.Memory
|
||||
userdataStore common.DataStore
|
||||
adminstore *utils.AdminStore
|
||||
flagManager *asm.FlagParser
|
||||
accountService remote.AccountServiceInterface
|
||||
prefixDb storage.PrefixDb
|
||||
}
|
||||
|
||||
func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService remote.AccountServiceInterface) (*Handlers, error) {
|
||||
func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface) (*Handlers, error) {
|
||||
if userdataStore == nil {
|
||||
return nil, fmt.Errorf("cannot create handler with nil userdata store")
|
||||
}
|
||||
@@ -81,21 +88,24 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService r
|
||||
h := &Handlers{
|
||||
userdataStore: userDb,
|
||||
flagManager: appFlags,
|
||||
adminstore: adminstore,
|
||||
accountService: accountService,
|
||||
prefixDb: prefixDb,
|
||||
}
|
||||
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
|
||||
func isValidPIN(pin string) bool {
|
||||
match, _ := regexp.MatchString(pinPattern, pin)
|
||||
return match
|
||||
}
|
||||
|
||||
func isValidPhoneNumber(phonenumber string) bool {
|
||||
match, _ := regexp.MatchString(phoneRegex, phonenumber)
|
||||
return match
|
||||
}
|
||||
|
||||
func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers {
|
||||
if h.pe != nil {
|
||||
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) {
|
||||
var r resource.Result
|
||||
|
||||
if h.pe == nil {
|
||||
logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
h.st = h.pe.GetState()
|
||||
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 {
|
||||
logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca)
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
res := resource.Result{}
|
||||
_, 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")
|
||||
|
||||
accountPIN := string(input)
|
||||
|
||||
// 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)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
|
||||
|
||||
store := h.userdataStore
|
||||
err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(accountPIN))
|
||||
if err != nil {
|
||||
@@ -254,6 +297,29 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
|
||||
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) {
|
||||
var res resource.Result
|
||||
sessionId, ok := ctx.Value("SessionId").(string)
|
||||
@@ -298,7 +364,6 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if bytes.Equal(input, temporaryPin) {
|
||||
res.FlagSet = []uint32{flag_valid_pin}
|
||||
res.FlagReset = []uint32{flag_pin_mismatch}
|
||||
@@ -513,6 +578,14 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt
|
||||
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.
|
||||
func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||
var res resource.Result
|
||||
@@ -596,12 +669,14 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b
|
||||
if !ok {
|
||||
return res, fmt.Errorf("missing session")
|
||||
}
|
||||
|
||||
store := h.userdataStore
|
||||
publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey))
|
||||
|
||||
if err != nil {
|
||||
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||
return res, err
|
||||
@@ -656,7 +731,6 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res
|
||||
var err error
|
||||
|
||||
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
|
||||
|
||||
date := string(input)
|
||||
_, err = strconv.Atoi(date)
|
||||
if err != nil {
|
||||
@@ -679,7 +753,6 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by
|
||||
var res resource.Result
|
||||
|
||||
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
|
||||
|
||||
res.FlagReset = append(res.FlagReset, flag_incorrect_date_format)
|
||||
return res, nil
|
||||
}
|
||||
@@ -761,6 +834,67 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input
|
||||
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.
|
||||
func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||
var res resource.Result
|
||||
@@ -932,6 +1066,22 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (
|
||||
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)
|
||||
func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||
var res resource.Result
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestNewHandlers(t *testing.T) {
|
||||
}
|
||||
t.Run("Valid UserDataStore", func(t *testing.T) {
|
||||
mockStore := &mocks.MockUserDataStore{}
|
||||
handlers, err := NewHandlers(fm.parser, mockStore, &accountService)
|
||||
handlers, err := NewHandlers(fm.parser, mockStore, nil, &accountService)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestNewHandlers(t *testing.T) {
|
||||
t.Run("Nil UserDataStore", func(t *testing.T) {
|
||||
appFlags := &asm.FlagParser{}
|
||||
|
||||
handlers, err := NewHandlers(appFlags, nil, &accountService)
|
||||
handlers, err := NewHandlers(appFlags, nil, nil, &accountService)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected an error, got none")
|
||||
|
||||
Reference in New Issue
Block a user