fix: validate display name on account creation (#4994)
This commit is contained in:
parent
223a1d759e
commit
e4c1abb5ce
|
@ -2,11 +2,19 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
"github.com/status-im/status-go/protocol/identity/alias"
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrInvalidDisplayNameRegExp = errors.New("only letters, numbers, underscores and hyphens allowed")
|
||||||
|
var ErrInvalidDisplayNameEthSuffix = errors.New(`usernames ending with "eth" are not allowed`)
|
||||||
|
var ErrInvalidDisplayNameNotAllowed = errors.New("name is not allowed")
|
||||||
|
|
||||||
func RecoverKey(m *protobuf.ApplicationMetadataMessage) (*ecdsa.PublicKey, error) {
|
func RecoverKey(m *protobuf.ApplicationMetadataMessage) (*ecdsa.PublicKey, error) {
|
||||||
if m.Signature == nil {
|
if m.Signature == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -22,3 +30,28 @@ func RecoverKey(m *protobuf.ApplicationMetadataMessage) (*ecdsa.PublicKey, error
|
||||||
|
|
||||||
return recoveredKey, nil
|
return recoveredKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidateDisplayName(displayName *string) error {
|
||||||
|
name := strings.TrimSpace(*displayName)
|
||||||
|
*displayName = name
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ^[\\w-\\s]{5,24}$ to allow spaces
|
||||||
|
if match, _ := regexp.MatchString("^[\\w-\\s]{5,24}$", name); !match {
|
||||||
|
return ErrInvalidDisplayNameRegExp
|
||||||
|
}
|
||||||
|
|
||||||
|
// .eth should not happen due to the regexp above, but let's keep it here in case the regexp is changed in the future
|
||||||
|
if strings.HasSuffix(name, "_eth") || strings.HasSuffix(name, ".eth") || strings.HasSuffix(name, "-eth") {
|
||||||
|
return ErrInvalidDisplayNameEthSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias.IsAlias(name) {
|
||||||
|
return ErrInvalidDisplayNameNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -266,24 +266,26 @@ func AssetNames() []string {
|
||||||
|
|
||||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
var _bindata = map[string]func() (*asset, error){
|
var _bindata = map[string]func() (*asset, error){
|
||||||
"_assets/tests/1.gif": _assetsTests1Gif,
|
"_assets/tests/1.gif": _assetsTests1Gif,
|
||||||
"_assets/tests/2x1.png": _assetsTests2x1Png,
|
"_assets/tests/2x1.png": _assetsTests2x1Png,
|
||||||
"_assets/tests/spin.gif": _assetsTestsSpinGif,
|
"_assets/tests/spin.gif": _assetsTestsSpinGif,
|
||||||
"_assets/tests/qr/QRWithLogo.png": _assetsTestsQrQrwithlogoPng,
|
"_assets/tests/qr/QRWithLogo.png": _assetsTestsQrQrwithlogoPng,
|
||||||
"_assets/tests/qr/defaultQR.png": _assetsTestsQrDefaultqrPng,
|
"_assets/tests/qr/defaultQR.png": _assetsTestsQrDefaultqrPng,
|
||||||
"_assets/tests/qr/status.png": _assetsTestsQrStatusPng,
|
"_assets/tests/qr/status.png": _assetsTestsQrStatusPng,
|
||||||
"_assets/tests/wikipedia.ico": _assetsTestsWikipediaIco,
|
"_assets/tests/wikipedia.ico": _assetsTestsWikipediaIco,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetDir returns the file names below a certain
|
// AssetDir returns the file names below a certain
|
||||||
// directory embedded in the file by go-bindata.
|
// directory embedded in the file by go-bindata.
|
||||||
// For example if you run go-bindata on data/... and data contains the
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
// following hierarchy:
|
// following hierarchy:
|
||||||
// data/
|
//
|
||||||
// foo.txt
|
// data/
|
||||||
// img/
|
// foo.txt
|
||||||
// a.png
|
// img/
|
||||||
// b.png
|
// a.png
|
||||||
|
// b.png
|
||||||
|
//
|
||||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||||
|
@ -314,17 +316,18 @@ type bintree struct {
|
||||||
Func func() (*asset, error)
|
Func func() (*asset, error)
|
||||||
Children map[string]*bintree
|
Children map[string]*bintree
|
||||||
}
|
}
|
||||||
|
|
||||||
var _bintree = &bintree{nil, map[string]*bintree{
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
"_assets": &bintree{nil, map[string]*bintree{
|
"_assets": &bintree{nil, map[string]*bintree{
|
||||||
"tests": &bintree{nil, map[string]*bintree{
|
"tests": &bintree{nil, map[string]*bintree{
|
||||||
"1.gif": &bintree{_assetsTests1Gif, map[string]*bintree{}},
|
"1.gif": &bintree{_assetsTests1Gif, map[string]*bintree{}},
|
||||||
"2x1.png": &bintree{_assetsTests2x1Png, map[string]*bintree{}},
|
"2x1.png": &bintree{_assetsTests2x1Png, map[string]*bintree{}},
|
||||||
"qr": &bintree{nil, map[string]*bintree{
|
"qr": &bintree{nil, map[string]*bintree{
|
||||||
"QRWithLogo.png": &bintree{_assetsTestsQrQrwithlogoPng, map[string]*bintree{}},
|
"QRWithLogo.png": &bintree{_assetsTestsQrQrwithlogoPng, map[string]*bintree{}},
|
||||||
"defaultQR.png": &bintree{_assetsTestsQrDefaultqrPng, map[string]*bintree{}},
|
"defaultQR.png": &bintree{_assetsTestsQrDefaultqrPng, map[string]*bintree{}},
|
||||||
"status.png": &bintree{_assetsTestsQrStatusPng, map[string]*bintree{}},
|
"status.png": &bintree{_assetsTestsQrStatusPng, map[string]*bintree{}},
|
||||||
}},
|
}},
|
||||||
"spin.gif": &bintree{_assetsTestsSpinGif, map[string]*bintree{}},
|
"spin.gif": &bintree{_assetsTestsSpinGif, map[string]*bintree{}},
|
||||||
"wikipedia.ico": &bintree{_assetsTestsWikipediaIco, map[string]*bintree{}},
|
"wikipedia.ico": &bintree{_assetsTestsWikipediaIco, map[string]*bintree{}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
|
@ -376,4 +379,3 @@ func _filePath(dir, name string) string {
|
||||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
utils "github.com/status-im/status-go/common"
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
"github.com/status-im/status-go/protocol/v1"
|
"github.com/status-im/status-go/protocol/v1"
|
||||||
)
|
)
|
||||||
|
@ -343,7 +344,7 @@ func ValidateReceivedChatMessage(message *protobuf.ChatMessage, whisperTimestamp
|
||||||
return errors.New("mutual state event system message content type not allowed")
|
return errors.New("mutual state event system message content type not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ValidateDisplayName(&message.DisplayName); err != nil {
|
if err := utils.ValidateDisplayName(&message.DisplayName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
utils "github.com/status-im/status-go/common"
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/images"
|
"github.com/status-im/status-go/images"
|
||||||
|
@ -1212,7 +1213,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message *pr
|
||||||
return ErrMessageNotAllowed
|
return ErrMessageNotAllowed
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = ValidateDisplayName(&message.DisplayName); err != nil {
|
if err = utils.ValidateDisplayName(&message.DisplayName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3023,7 +3024,7 @@ func (m *Messenger) HandleChatIdentity(state *ReceivedMessageState, ci *protobuf
|
||||||
}
|
}
|
||||||
|
|
||||||
if clockChanged {
|
if clockChanged {
|
||||||
if err = ValidateDisplayName(&ci.DisplayName); err != nil {
|
if err = utils.ValidateDisplayName(&ci.DisplayName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,14 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
utils "github.com/status-im/status-go/common"
|
||||||
"github.com/status-im/status-go/multiaccounts/settings"
|
"github.com/status-im/status-go/multiaccounts/settings"
|
||||||
sociallinkssettings "github.com/status-im/status-go/multiaccounts/settings_social_links"
|
sociallinkssettings "github.com/status-im/status-go/multiaccounts/settings_social_links"
|
||||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||||
"github.com/status-im/status-go/protocol/identity"
|
"github.com/status-im/status-go/protocol/identity"
|
||||||
"github.com/status-im/status-go/protocol/identity/alias"
|
|
||||||
"github.com/status-im/status-go/server"
|
"github.com/status-im/status-go/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,38 +20,10 @@ const (
|
||||||
maxSocialLinkTextLength = 24
|
maxSocialLinkTextLength = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrInvalidDisplayNameRegExp = errors.New("only letters, numbers, underscores and hyphens allowed")
|
|
||||||
var ErrInvalidDisplayNameEthSuffix = errors.New(`usernames ending with "eth" are not allowed`)
|
|
||||||
var ErrInvalidDisplayNameNotAllowed = errors.New("name is not allowed")
|
|
||||||
var ErrInvalidBioLength = errors.New("invalid bio length")
|
var ErrInvalidBioLength = errors.New("invalid bio length")
|
||||||
var ErrInvalidSocialLinkTextLength = errors.New("invalid social link text length")
|
var ErrInvalidSocialLinkTextLength = errors.New("invalid social link text length")
|
||||||
var ErrDisplayNameDupeOfCommunityMember = errors.New("display name duplicates on of community members")
|
var ErrDisplayNameDupeOfCommunityMember = errors.New("display name duplicates on of community members")
|
||||||
|
|
||||||
func ValidateDisplayName(displayName *string) error {
|
|
||||||
name := strings.TrimSpace(*displayName)
|
|
||||||
*displayName = name
|
|
||||||
|
|
||||||
if name == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ^[\\w-\\s]{5,24}$ to allow spaces
|
|
||||||
if match, _ := regexp.MatchString("^[\\w-\\s]{5,24}$", name); !match {
|
|
||||||
return ErrInvalidDisplayNameRegExp
|
|
||||||
}
|
|
||||||
|
|
||||||
// .eth should not happen due to the regexp above, but let's keep it here in case the regexp is changed in the future
|
|
||||||
if strings.HasSuffix(name, "_eth") || strings.HasSuffix(name, ".eth") || strings.HasSuffix(name, "-eth") {
|
|
||||||
return ErrInvalidDisplayNameEthSuffix
|
|
||||||
}
|
|
||||||
|
|
||||||
if alias.IsAlias(name) {
|
|
||||||
return ErrInvalidDisplayNameNotAllowed
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Messenger) SetDisplayName(displayName string) error {
|
func (m *Messenger) SetDisplayName(displayName string) error {
|
||||||
currDisplayName, err := m.settings.DisplayName()
|
currDisplayName, err := m.settings.DisplayName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,7 +34,7 @@ func (m *Messenger) SetDisplayName(displayName string) error {
|
||||||
return nil // Do nothing
|
return nil // Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = ValidateDisplayName(&displayName); err != nil {
|
if err = utils.ValidateDisplayName(&displayName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
utils "github.com/status-im/status-go/common"
|
||||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||||
"github.com/status-im/status-go/protocol/tt"
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
|
@ -182,3 +183,42 @@ func (s *MessengerProfileDisplayNameHandlerSuite) TestDisplayNameSync() {
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Equal(testDisplayName, dbProfileKp.Name)
|
s.Require().Equal(testDisplayName, dbProfileKp.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MessengerProfileDisplayNameHandlerSuite) TestDisplayNameRestrictions() {
|
||||||
|
// check display name for the created instance
|
||||||
|
displayName, err := s.m.settings.DisplayName()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(DefaultProfileDisplayName, displayName)
|
||||||
|
|
||||||
|
// add profile keypair
|
||||||
|
profileKp := accounts.GetProfileKeypairForTest(true, false, false)
|
||||||
|
profileKp.KeyUID = s.m.account.KeyUID
|
||||||
|
profileKp.Name = DefaultProfileDisplayName
|
||||||
|
profileKp.Accounts[0].KeyUID = s.m.account.KeyUID
|
||||||
|
|
||||||
|
err = s.m.settings.SaveOrUpdateKeypair(profileKp)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
setInvalidName := func(invalidName string, expectedErr error) {
|
||||||
|
err = s.m.SetDisplayName(invalidName)
|
||||||
|
s.Require().ErrorIs(err, expectedErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
setInvalidName("test.eth", utils.ErrInvalidDisplayNameRegExp)
|
||||||
|
setInvalidName("test-eth", utils.ErrInvalidDisplayNameEthSuffix)
|
||||||
|
setInvalidName("test_eth", utils.ErrInvalidDisplayNameEthSuffix)
|
||||||
|
|
||||||
|
setInvalidName("dot.not", utils.ErrInvalidDisplayNameRegExp)
|
||||||
|
setInvalidName("t", utils.ErrInvalidDisplayNameRegExp)
|
||||||
|
setInvalidName("tt", utils.ErrInvalidDisplayNameRegExp)
|
||||||
|
setInvalidName("ttt", utils.ErrInvalidDisplayNameRegExp)
|
||||||
|
setInvalidName("tttt", utils.ErrInvalidDisplayNameRegExp)
|
||||||
|
setInvalidName("name is bigger than 24 symb", utils.ErrInvalidDisplayNameRegExp)
|
||||||
|
|
||||||
|
err = s.m.SetDisplayName("name with space")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
displayName, err = s.m.settings.DisplayName()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal("name with space", displayName)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package requests
|
package requests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
utils "github.com/status-im/status-go/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrCreateAccountInvalidDisplayName = errors.New("create-account: invalid display name")
|
var ErrCreateAccountInvalidDisplayName = errors.New("create-account: invalid display name")
|
||||||
|
@ -87,6 +89,10 @@ func (c *CreateAccount) Validate(validation *CreateAccountValidation) error {
|
||||||
return ErrCreateAccountInvalidDisplayName
|
return ErrCreateAccountInvalidDisplayName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := utils.ValidateDisplayName(&c.DisplayName); err != nil {
|
||||||
|
return errors.Wrap(ErrCreateAccountInvalidDisplayName, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
if len(c.Password) == 0 {
|
if len(c.Password) == 0 {
|
||||||
return ErrCreateAccountInvalidPassword
|
return ErrCreateAccountInvalidPassword
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue