Compare commits
11 Commits
3de46cef5e
...
124049c924
Author | SHA1 | Date | |
---|---|---|---|
124049c924 | |||
5fd3eb3c29 | |||
d83962c0ba | |||
41da099933 | |||
c9bb93ede6 | |||
ca13d9155c | |||
e338ce0025 | |||
b97965193b | |||
aec0abb2b6 | |||
26073c8000 | |||
e4c2f644f3 |
@ -16,3 +16,7 @@ CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create
|
||||
TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/
|
||||
BALANCE_URL=https://custodial.sarafu.africa/api/account/status/
|
||||
TRACK_URL=http://localhost:5003/api/v2/account/status
|
||||
|
||||
|
||||
#numbers with privileges to reset others pin
|
||||
ADMIN_NUMBERS=254051722XXX,255012221XXX
|
@ -15,6 +15,7 @@ import (
|
||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||
"git.grassecon.net/urdt/ussd/internal/handlers/server"
|
||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||
"git.grassecon.net/urdt/ussd/internal/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -48,6 +49,9 @@ func main() {
|
||||
ctx = context.WithValue(ctx, "Database", database)
|
||||
pfp := path.Join(scriptDir, "pp.csv")
|
||||
|
||||
as := utils.NewAdminStore("admin_numbers.txt")
|
||||
as.Seed()
|
||||
|
||||
cfg := engine.Config{
|
||||
Root: "root",
|
||||
SessionId: sessionId,
|
||||
|
@ -54,7 +54,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
|
||||
}
|
||||
|
||||
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, accountService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -98,6 +98,10 @@ func (ls *LocalHandlerService) GetHandler(accountService server.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)
|
||||
|
||||
return ussdHandlers, nil
|
||||
}
|
||||
|
@ -27,11 +27,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
logg = logging.NewVanilla().WithDomain("ussdmenuhandler")
|
||||
scriptDir = path.Join("services", "registration")
|
||||
translationDir = path.Join(scriptDir, "locale")
|
||||
okResponse *api.OKResponse
|
||||
errResponse *api.ErrResponse
|
||||
logg = logging.NewVanilla().WithDomain("ussdmenuhandler")
|
||||
scriptDir = path.Join("services", "registration")
|
||||
translationDir = path.Join(scriptDir, "locale")
|
||||
PINChangePrivilege byte = 1
|
||||
okResponse *api.OKResponse
|
||||
errResponse *api.ErrResponse
|
||||
)
|
||||
|
||||
// FlagManager handles centralized flag management
|
||||
@ -100,13 +101,27 @@ 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")
|
||||
|
||||
number, _ := strconv.ParseInt(sessionId, 10, 64)
|
||||
as := utils.NewAdminStore("admin_numbers.txt")
|
||||
isAdmin, _ := as.IsAdmin(number)
|
||||
|
||||
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")
|
||||
@ -189,6 +204,26 @@ 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
|
||||
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN)
|
||||
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)
|
||||
@ -284,7 +319,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}
|
||||
@ -362,6 +396,7 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou
|
||||
if !ok {
|
||||
return res, fmt.Errorf("missing session")
|
||||
}
|
||||
|
||||
if len(input) == 4 {
|
||||
yob := string(input)
|
||||
store := h.userdataStore
|
||||
@ -444,6 +479,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
|
||||
@ -527,11 +570,13 @@ 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, utils.DATA_PUBLIC_KEY)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey))
|
||||
if err != nil {
|
||||
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||
@ -588,7 +633,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 {
|
||||
@ -694,6 +738,22 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *Handlers) ValidateBlockedNumber(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")
|
||||
}
|
||||
blockedNumber := string(input)
|
||||
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
|
||||
@ -865,6 +925,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
|
||||
@ -1102,7 +1178,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
err = prefixdb.Put(ctx, []byte("bal"), []byte(voucherBalanceList))
|
||||
if err != nil {
|
||||
return res, nil
|
||||
|
80
internal/utils/adminstore.go
Normal file
80
internal/utils/adminstore.go
Normal file
@ -0,0 +1,80 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.grassecon.net/urdt/ussd/initializers"
|
||||
)
|
||||
|
||||
type AdminStore struct {
|
||||
filePath string
|
||||
}
|
||||
|
||||
// Creates a new Admin store
|
||||
func NewAdminStore(filePath string) *AdminStore {
|
||||
return &AdminStore{filePath: filePath}
|
||||
}
|
||||
|
||||
// Seed initializes a list of phonenumbers with admin privileges
|
||||
func (as *AdminStore) Seed() error {
|
||||
var adminNumbers []int64
|
||||
|
||||
numbersEnv := initializers.GetEnv("ADMIN_NUMBERS", "")
|
||||
for _, numStr := range strings.Split(numbersEnv, ",") {
|
||||
if num, err := strconv.ParseInt(strings.TrimSpace(numStr), 10, 64); err == nil {
|
||||
adminNumbers = append(adminNumbers, num)
|
||||
} else {
|
||||
log.Printf("Skipping invalid number: %s", numStr)
|
||||
}
|
||||
}
|
||||
file, err := os.Create(as.filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
writer := bufio.NewWriter(file)
|
||||
for _, num := range adminNumbers {
|
||||
_, err := writer.WriteString(strconv.FormatInt(num, 10) + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return writer.Flush()
|
||||
}
|
||||
|
||||
func (as *AdminStore) load() ([]int64, error) {
|
||||
file, err := os.Open(as.filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var numbers []int64
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
num, err := strconv.ParseInt(scanner.Text(), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numbers = append(numbers, num)
|
||||
}
|
||||
return numbers, scanner.Err()
|
||||
}
|
||||
|
||||
func (as *AdminStore) IsAdmin(phoneNumber int64) (bool, error) {
|
||||
phoneNumbers, err := as.load()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, phonenumber := range phoneNumbers {
|
||||
if phonenumber == phoneNumber {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
@ -28,6 +28,7 @@ const (
|
||||
DATA_ACTIVE_SYM
|
||||
DATA_TEMPORARY_BAL
|
||||
DATA_ACTIVE_BAL
|
||||
DATA_BLOCKED_NUMBER
|
||||
)
|
||||
|
||||
func typToBytes(typ DataTyp) []byte {
|
||||
|
@ -1,9 +1,8 @@
|
||||
CATCH incorrect_pin flag_incorrect_pin 1
|
||||
CATCH profile_update_success flag_allow_update 1
|
||||
LOAD save_familyname 0
|
||||
RELOAD save_familyname
|
||||
MOUT back 0
|
||||
HALT
|
||||
LOAD save_familyname 0
|
||||
RELOAD save_familyname
|
||||
INCMP _ 0
|
||||
INCMP pin_entry *
|
||||
|
@ -2,6 +2,6 @@ MOUT change_pin 1
|
||||
MOUT reset_pin 2
|
||||
MOUT back 0
|
||||
HALT
|
||||
INCMP _ 0
|
||||
INCMP my_account 0
|
||||
INCMP old_pin 1
|
||||
INCMP enter_other_number 2
|
||||
|
Loading…
Reference in New Issue
Block a user