PLT-5458: If someone posts a channel link to channel_A that you don't belong to, it doesn't render properly (#7833)

* add channel link hints to post props

* optimization

* update regex, add unit test

* fix rebase issue
This commit is contained in:
Chris
2017-11-28 15:02:56 -06:00
committed by Christopher Speller
parent 27ba68a789
commit b87fae646a
9 changed files with 252 additions and 0 deletions

View File

@@ -586,6 +586,65 @@ func (s SqlChannelStore) GetByName(teamId string, name string, allowFromCache bo
return s.getByName(teamId, name, false, allowFromCache)
}
func (s SqlChannelStore) GetByNames(teamId string, names []string, allowFromCache bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var channels []*model.Channel
if allowFromCache {
var misses []string
visited := make(map[string]struct{})
for _, name := range names {
if _, ok := visited[name]; ok {
continue
}
visited[name] = struct{}{}
if cacheItem, ok := channelByNameCache.Get(teamId + name); ok {
if s.metrics != nil {
s.metrics.IncrementMemCacheHitCounter("Channel By Name")
}
channels = append(channels, cacheItem.(*model.Channel))
} else {
if s.metrics != nil {
s.metrics.IncrementMemCacheMissCounter("Channel By Name")
}
misses = append(misses, name)
}
}
names = misses
}
if len(names) > 0 {
props := map[string]interface{}{}
var namePlaceholders []string
for _, name := range names {
key := fmt.Sprintf("Name%v", len(namePlaceholders))
props[key] = name
namePlaceholders = append(namePlaceholders, ":"+key)
}
var query string
if teamId == "" {
query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND DeleteAt = 0`
} else {
props["TeamId"] = teamId
query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND TeamId = :TeamId AND DeleteAt = 0`
}
var dbChannels []*model.Channel
if _, err := s.GetReplica().Select(&dbChannels, query, props); err != nil && err != sql.ErrNoRows {
result.Err = model.NewAppError("SqlChannelStore.GetByName", "store.sql_channel.get_by_name.existing.app_error", nil, "teamId="+teamId+", "+err.Error(), http.StatusInternalServerError)
return
}
for _, channel := range dbChannels {
channelByNameCache.AddWithExpiresInSecs(teamId+channel.Name, channel, CHANNEL_CACHE_SEC)
channels = append(channels, channel)
}
}
result.Data = channels
})
}
func (s SqlChannelStore) GetByNameIncludeDeleted(teamId string, name string, allowFromCache bool) store.StoreChannel {
return s.getByName(teamId, name, true, allowFromCache)
}

View File

@@ -119,6 +119,7 @@ type ChannelStore interface {
PermanentDeleteByTeam(teamId string) StoreChannel
PermanentDelete(channelId string) StoreChannel
GetByName(team_id string, name string, allowFromCache bool) StoreChannel
GetByNames(team_id string, names []string, allowFromCache bool) StoreChannel
GetByNameIncludeDeleted(team_id string, name string, allowFromCache bool) StoreChannel
GetDeletedByName(team_id string, name string) StoreChannel
GetDeleted(team_id string, offset int, limit int) StoreChannel

View File

@@ -4,10 +4,12 @@
package storetest
import (
"sort"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
@@ -24,6 +26,7 @@ func TestChannelStore(t *testing.T, ss store.Store) {
t.Run("Restore", func(t *testing.T) { testChannelStoreRestore(t, ss) })
t.Run("Delete", func(t *testing.T) { testChannelStoreDelete(t, ss) })
t.Run("GetByName", func(t *testing.T) { testChannelStoreGetByName(t, ss) })
t.Run("GetByNames", func(t *testing.T) { testChannelStoreGetByNames(t, ss) })
t.Run("GetDeletedByName", func(t *testing.T) { testChannelStoreGetDeletedByName(t, ss) })
t.Run("GetDeleted", func(t *testing.T) { testChannelStoreGetDeleted(t, ss) })
t.Run("ChannelMemberStore", func(t *testing.T) { testChannelMemberStore(t, ss) })
@@ -550,6 +553,57 @@ func testChannelStoreGetByName(t *testing.T, ss store.Store) {
}
}
func testChannelStoreGetByNames(t *testing.T, ss store.Store) {
o1 := model.Channel{
TeamId: model.NewId(),
DisplayName: "Name",
Name: "zz" + model.NewId() + "b",
Type: model.CHANNEL_OPEN,
}
store.Must(ss.Channel().Save(&o1, -1))
o2 := model.Channel{
TeamId: o1.TeamId,
DisplayName: "Name",
Name: "zz" + model.NewId() + "b",
Type: model.CHANNEL_OPEN,
}
store.Must(ss.Channel().Save(&o2, -1))
for index, tc := range []struct {
TeamId string
Names []string
ExpectedIds []string
}{
{o1.TeamId, []string{o1.Name}, []string{o1.Id}},
{o1.TeamId, []string{o1.Name, o2.Name}, []string{o1.Id, o2.Id}},
{o1.TeamId, nil, nil},
{o1.TeamId, []string{"foo"}, nil},
{o1.TeamId, []string{o1.Name, "foo", o2.Name, o2.Name}, []string{o1.Id, o2.Id}},
{"", []string{o1.Name, "foo", o2.Name, o2.Name}, []string{o1.Id, o2.Id}},
{"asd", []string{o1.Name, "foo", o2.Name, o2.Name}, nil},
} {
r := <-ss.Channel().GetByNames(tc.TeamId, tc.Names, true)
require.Nil(t, r.Err)
channels := r.Data.([]*model.Channel)
var ids []string
for _, channel := range channels {
ids = append(ids, channel.Id)
}
sort.Strings(ids)
sort.Strings(tc.ExpectedIds)
assert.Equal(t, tc.ExpectedIds, ids, "tc %v", index)
}
store.Must(ss.Channel().Delete(o1.Id, model.GetMillis()))
store.Must(ss.Channel().Delete(o2.Id, model.GetMillis()))
r := <-ss.Channel().GetByNames(o1.TeamId, []string{o1.Name}, false)
require.Nil(t, r.Err)
channels := r.Data.([]*model.Channel)
assert.Len(t, channels, 0)
}
func testChannelStoreGetDeletedByName(t *testing.T, ss store.Store) {
o1 := model.Channel{}
o1.TeamId = model.NewId()

View File

@@ -189,6 +189,22 @@ func (_m *ChannelStore) GetByNameIncludeDeleted(team_id string, name string, all
return r0
}
// GetByNames provides a mock function with given fields: team_id, names, allowFromCache
func (_m *ChannelStore) GetByNames(team_id string, names []string, allowFromCache bool) store.StoreChannel {
ret := _m.Called(team_id, names, allowFromCache)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, []string, bool) store.StoreChannel); ok {
r0 = rf(team_id, names, allowFromCache)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetChannelCounts provides a mock function with given fields: teamId, userId
func (_m *ChannelStore) GetChannelCounts(teamId string, userId string) store.StoreChannel {
ret := _m.Called(teamId, userId)