forked from urdt/ussd
Validate aliases, addresses and phone numbers in the send menu (#176)
- Update the phone number regex - Check whether the recipient is a valid phone number, alias or address Reviewed-on: urdt/ussd#176 Co-authored-by: alfred-mk <alfredmwaik@gmail.com> Co-committed-by: alfred-mk <alfredmwaik@gmail.com>
This commit is contained in:
@@ -35,12 +35,17 @@ var (
|
||||
errResponse *api.ErrResponse
|
||||
)
|
||||
|
||||
// Define the regex patterns as constants
|
||||
// Define the regex patterns as constants
|
||||
const (
|
||||
phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}`
|
||||
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
|
||||
}
|
||||
|
||||
// FlagManager handles centralized flag management
|
||||
type FlagManager struct {
|
||||
parser *asm.FlagParser
|
||||
@@ -95,17 +100,6 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// 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")
|
||||
@@ -877,7 +871,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input
|
||||
}
|
||||
blockedNumber := string(input)
|
||||
_, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY)
|
||||
if !isValidPhoneNumber(blockedNumber) {
|
||||
if !common.IsValidPhoneNumber(blockedNumber) {
|
||||
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
|
||||
return res, nil
|
||||
}
|
||||
@@ -898,10 +892,9 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ValidateRecipient validates that the given input is a valid phone number.
|
||||
// ValidateRecipient validates that the given input is valid.
|
||||
func (h *Handlers) ValidateRecipient(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)
|
||||
@@ -909,13 +902,16 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by
|
||||
return res, fmt.Errorf("missing session")
|
||||
}
|
||||
|
||||
recipient := string(input)
|
||||
|
||||
flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient")
|
||||
flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite")
|
||||
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
|
||||
|
||||
recipient := string(input)
|
||||
|
||||
if recipient != "0" {
|
||||
if !isValidPhoneNumber(recipient) {
|
||||
recipientType, err := common.CheckRecipient(recipient)
|
||||
if err != nil {
|
||||
// Invalid recipient format (not a phone number, address, or valid alias format)
|
||||
res.FlagSet = append(res.FlagSet, flag_invalid_recipient)
|
||||
res.Content = recipient
|
||||
|
||||
@@ -929,25 +925,61 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by
|
||||
return res, err
|
||||
}
|
||||
|
||||
publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY)
|
||||
if err != nil {
|
||||
if db.IsNotFound(err) {
|
||||
logg.InfoCtxf(ctx, "Unregistered number")
|
||||
|
||||
res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite)
|
||||
res.Content = recipient
|
||||
|
||||
return res, nil
|
||||
switch recipientType {
|
||||
case "phone number":
|
||||
// format the phone number
|
||||
formattedNumber, err := common.FormatPhoneNumber(recipient)
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "Failed to format the phone number: %s", recipient, "error", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err)
|
||||
return res, err
|
||||
}
|
||||
// Check if the phone number is registered
|
||||
publicKey, err := store.ReadEntry(ctx, formattedNumber, common.DATA_PUBLIC_KEY)
|
||||
if err != nil {
|
||||
if db.IsNotFound(err) {
|
||||
logg.InfoCtxf(ctx, "Unregistered phone number: %s", recipient)
|
||||
res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite)
|
||||
res.Content = recipient
|
||||
return res, nil
|
||||
}
|
||||
|
||||
err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, publicKey)
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", string(publicKey), "error", err)
|
||||
return res, nil
|
||||
logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Save the publicKey as the recipient
|
||||
err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, publicKey)
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", string(publicKey), "error", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
case "address":
|
||||
// Save the valid Ethereum address as the recipient
|
||||
err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", recipient, "error", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
case "alias":
|
||||
// Call the API to validate and retrieve the address for the alias
|
||||
r, aliasErr := h.accountService.CheckAliasAddress(ctx, recipient)
|
||||
if aliasErr != nil {
|
||||
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||
res.Content = recipient
|
||||
|
||||
logg.ErrorCtxf(ctx, "failed on CheckAliasAddress", "error", aliasErr)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Alias validation succeeded, save the Ethereum address
|
||||
err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(r.Address))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", r.Address, "error", err)
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,3 +47,8 @@ func (m *MockAccountService) TokenTransfer(ctx context.Context, amount, from, to
|
||||
args := m.Called()
|
||||
return args.Get(0).(*models.TokenTransferResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).(*dataserviceapi.AliasAddress), args.Error(1)
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey
|
||||
}
|
||||
|
||||
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
|
||||
return []dataserviceapi.TokenHoldings {
|
||||
dataserviceapi.TokenHoldings {
|
||||
return []dataserviceapi.TokenHoldings{
|
||||
dataserviceapi.TokenHoldings{
|
||||
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
|
||||
TokenSymbol: "SRF",
|
||||
TokenDecimals: "6",
|
||||
@@ -56,3 +56,7 @@ func (tas *TestAccountService) TokenTransfer(ctx context.Context, amount, from,
|
||||
TrackingId: "e034d147-747d-42ea-928d-b5a7cb3426af",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m TestAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) {
|
||||
return &dataserviceapi.AliasAddress{}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user