From 64021bf5c4810546cd9a8aa22d263b0365e98d54 Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 7 Oct 2022 18:32:11 +0530 Subject: [PATCH] [MM-46644] Auto respond message (#20900) Automatic Merge (cherry picked from commit 6e9b808efd7f6ded8d4de20bac98e087d82e0252) --- model/utils.go | 25 +++++++++++++++++++++++++ store/sqlstore/user_store.go | 27 +++++++++++++++++++++++++++ store/storetest/user_store.go | 26 ++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/model/utils.go b/model/utils.go index 82c5e0953a044..929b21553cffb 100644 --- a/model/utils.go +++ b/model/utils.go @@ -34,8 +34,11 @@ const ( NUMBERS = "0123456789" SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~" BinaryParamKey = "MM_BINARY_PARAMETERS" + maxPropSizeBytes = 1024 * 1024 ) +var ErrMaxPropSizeExceeded = fmt.Errorf("max prop size of %d exceeded", maxPropSizeBytes) + type StringInterface map[string]any type StringArray []string @@ -77,6 +80,14 @@ func (sa StringArray) Equals(input StringArray) bool { // Value converts StringArray to database value func (sa StringArray) Value() (driver.Value, error) { + sz := 0 + for i := range sa { + sz += len(sa[i]) + if sz > maxPropSizeBytes { + return nil, ErrMaxPropSizeExceeded + } + } + j, err := json.Marshal(sa) if err != nil { return nil, err @@ -127,6 +138,15 @@ func (m *StringMap) Scan(value any) error { func (m StringMap) Value() (driver.Value, error) { ok := m[BinaryParamKey] delete(m, BinaryParamKey) + + sz := 0 + for k := range m { + sz += len(k) + len(m[k]) + if sz > maxPropSizeBytes { + return nil, ErrMaxPropSizeExceeded + } + } + buf, err := json.Marshal(m) if err != nil { return nil, err @@ -182,6 +202,11 @@ func (si StringInterface) Value() (driver.Value, error) { if err != nil { return nil, err } + + if len(j) > maxPropSizeBytes { + return nil, ErrMaxPropSizeExceeded + } + // non utf8 characters are not supported https://mattermost.atlassian.net/browse/MM-41066 return string(j), err } diff --git a/store/sqlstore/user_store.go b/store/sqlstore/user_store.go index 405172ad21d1c..b4dd5ae87afa2 100644 --- a/store/sqlstore/user_store.go +++ b/store/sqlstore/user_store.go @@ -10,6 +10,7 @@ import ( "fmt" "sort" "strings" + "unicode/utf8" sq "github.com/mattermost/squirrel" "github.com/pkg/errors" @@ -17,6 +18,7 @@ import ( "github.com/mattermost/mattermost-server/v6/einterfaces" "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/store" ) @@ -59,7 +61,24 @@ func newSqlUserStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface) s return us } +func (us SqlUserStore) validateAutoResponderMessageSize(notifyProps model.StringMap) error { + if notifyProps != nil { + maxPostSize := us.Post().GetMaxPostSize() + msg := notifyProps[model.AutoResponderMessageNotifyProp] + msgSize := utf8.RuneCountInString(msg) + if msgSize > maxPostSize { + mlog.Warn("auto_responder_message has size restrictions", mlog.Int("max_characters", maxPostSize), mlog.Int("received_size", msgSize)) + return errors.New("Auto responder message size can't be more than the allowed Post size") + } + } + return nil +} + func (us SqlUserStore) insert(user *model.User) (sql.Result, error) { + if err := us.validateAutoResponderMessageSize(user.NotifyProps); err != nil { + return nil, err + } + query := `INSERT INTO Users (Id, CreateAt, UpdateAt, DeleteAt, Username, Password, AuthData, AuthService, Email, EmailVerified, Nickname, FirstName, LastName, Position, Roles, AllowMarketing, @@ -150,6 +169,10 @@ func (us SqlUserStore) Update(user *model.User, trustedUpdateData bool) (*model. return nil, err } + if err := us.validateAutoResponderMessageSize(user.NotifyProps); err != nil { + return nil, err + } + oldUser := model.User{} err := us.GetMasterX().Get(&oldUser, "SELECT * FROM Users WHERE Id=?", user.Id) if err != nil { @@ -228,6 +251,10 @@ func (us SqlUserStore) Update(user *model.User, trustedUpdateData bool) (*model. } func (us SqlUserStore) UpdateNotifyProps(userID string, props map[string]string) error { + if err := us.validateAutoResponderMessageSize(props); err != nil { + return err + } + buf, err := json.Marshal(props) if err != nil { return errors.Wrap(err, "failed marshalling session props") diff --git a/store/storetest/user_store.go b/store/storetest/user_store.go index 7214b98b0ba34..9853d1821c6c3 100644 --- a/store/storetest/user_store.go +++ b/store/storetest/user_store.go @@ -129,8 +129,18 @@ func testUserStoreSave(t *testing.T, ss store.Store) { require.Error(t, err, "should be unique username") u2.Username = "" - _, err = ss.User().Save(&u1) - require.Error(t, err, "should be unique username") + _, err = ss.User().Save(&u2) + require.Error(t, err, "should be non-empty username") + + u3 := model.User{ + Email: MakeEmail(), + Username: model.NewId(), + NotifyProps: make(map[string]string, 1), + } + maxPostSize := ss.Post().GetMaxPostSize() + u3.NotifyProps[model.AutoResponderMessageNotifyProp] = strings.Repeat("a", maxPostSize+1) + _, err = ss.User().Save(&u3) + require.Error(t, err, "auto responder message size should not be greater than maxPostSize") for i := 0; i < 49; i++ { u := model.User{ @@ -234,6 +244,18 @@ func testUserStoreUpdate(t *testing.T, ss store.Store) { uNew, err := ss.User().Get(context.Background(), u1.Id) require.NoError(t, err) assert.Equal(t, props, uNew.NotifyProps) + + u4 := model.User{ + Email: MakeEmail(), + Username: model.NewId(), + NotifyProps: make(map[string]string, 1), + } + maxPostSize := ss.Post().GetMaxPostSize() + u4.NotifyProps[model.AutoResponderMessageNotifyProp] = strings.Repeat("a", maxPostSize+1) + _, err = ss.User().Update(&u4, false) + require.Error(t, err, "auto responder message size should not be greater than maxPostSize") + err = ss.User().UpdateNotifyProps(u4.Id, u4.NotifyProps) + require.Error(t, err, "auto responder message size should not be greater than maxPostSize") } func testUserStoreUpdateUpdateAt(t *testing.T, ss store.Store) {