Files

110 lines
3.2 KiB
Go

// Package tel is an incomplete implementation of SMS or voice credential validator.
package tel
import (
"github.com/nyaruka/phonenumbers"
"github.com/tinode/chat/server/store"
t "github.com/tinode/chat/server/store/types"
)
const validatorName = "tel"
// Empty placeholder struct.
type validator struct {
DebugResponse string `json:"debug_response"`
MaxRetries int `json:"max_retries"`
}
// Init is a noop.
func (v *validator) Init(jsonconf string) error {
// Implement: Parse config and initialize SMS service.
v.MaxRetries = 1000
v.DebugResponse = "123456"
return nil
}
// PreCheck validates the credential and parameters without sending an SMS or making the call.
// If credential is valid it's formatted and prefixed with a tag namespace.
func (*validator) PreCheck(cred string, params map[string]interface{}) (string, error) {
countryCode, ok := params["countryCode"].(string)
if !ok {
countryCode = "US"
}
// Libphonenumber is broken by design: Parse will try to extract the number from any text.
if phonenumbers.VALID_PHONE_NUMBER_PATTERN.MatchString(cred) {
if num, err := phonenumbers.Parse(cred, countryCode); err == nil {
// It's a phone number. Not checking the length because phone numbers cannot be that long.
if phonenumbers.IsValidNumber(num) {
return validatorName + ":" + phonenumbers.Format(num, phonenumbers.E164), nil
}
}
}
return "", t.ErrMalformed
}
// Request sends a request for confirmation to the user: makes a record in DB and nothing else.
func (*validator) Request(user t.Uid, cred, lang, resp string, tmpToken []byte) (bool, error) {
// TODO: actually send a validation SMS or make a call to the provided `cred` here.
return true, nil
}
// ResetSecret sends a message with instructions for resetting an authentication secret.
func (*validator) ResetSecret(cred, scheme, lang string, tmpToken []byte, params map[string]interface{}) error {
// TODO: send SMS with rest instructions.
return nil
}
// Check checks validity of user's response.
func (v *validator) Check(user t.Uid, resp string) (string, error) {
cred, err := store.Users.GetActiveCred(user, validatorName)
if err != nil {
return "", err
}
if cred == nil {
// Request to validate non-existent credential.
return "", t.ErrNotFound
}
if cred.Retries > v.MaxRetries {
return "", t.ErrPolicy
}
if resp == "" {
return "", t.ErrCredentials
}
// Comparing with dummy response too.
if cred.Resp == resp || v.DebugResponse == resp {
// Valid response, save confirmation.
return cred.Value, store.Users.ConfirmCred(user, validatorName)
}
// Invalid response, increment fail counter, ignore possible error.
store.Users.FailCred(user, validatorName)
return "", t.ErrCredentials
}
// Delete deletes user's records. Returns deleted credentials.
func (*validator) Delete(user t.Uid) error {
return store.Users.DelCred(user, validatorName, "")
}
// Remove or disable the given record
func (*validator) Remove(user t.Uid, value string) error {
return store.Users.DelCred(user, validatorName, value)
}
// Implement sending a text message
func (*validator) send(to, body string) error {
return nil
}
func init() {
store.RegisterValidator(validatorName, &validator{})
}