MM-11575: change plugin nil semantics (#9212)
* change MessageWillBePosted nil return semantics * change FileWillBeUploaded nil return semantics * use LogDebug to verify plugin inputs vs. the confusing Delete(User|Team)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
1ecb98d9f5
Коммит
04749027f6
21
app/file.go
21
app/file.go
@@ -435,20 +435,27 @@ func (a *App) DoUploadFileExpectModification(now time.Time, rawTeamId string, ra
|
|||||||
}
|
}
|
||||||
|
|
||||||
if a.PluginsReady() {
|
if a.PluginsReady() {
|
||||||
|
var rejectionError *model.AppError
|
||||||
pluginContext := &plugin.Context{}
|
pluginContext := &plugin.Context{}
|
||||||
var rejectionReason string
|
|
||||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||||
var newBytes bytes.Buffer
|
var newBytes bytes.Buffer
|
||||||
info, rejectionReason = hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
|
replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
|
||||||
rejected := info == nil
|
if rejectionReason != "" {
|
||||||
if !rejected && newBytes.Len() != 0 {
|
rejectionError = model.NewAppError("DoUploadFile", "File rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if replacementInfo != nil {
|
||||||
|
info = replacementInfo
|
||||||
|
}
|
||||||
|
if newBytes.Len() != 0 {
|
||||||
data = newBytes.Bytes()
|
data = newBytes.Bytes()
|
||||||
info.Size = int64(len(data))
|
info.Size = int64(len(data))
|
||||||
}
|
}
|
||||||
return !rejected
|
|
||||||
|
return true
|
||||||
}, plugin.FileWillBeUploadedId)
|
}, plugin.FileWillBeUploadedId)
|
||||||
if info == nil {
|
if rejectionError != nil {
|
||||||
return nil, data, model.NewAppError("DoUploadFile", "File rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest)
|
return nil, data, rejectionError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func TestPluginDeadlock(t *testing.T) {
|
|||||||
|
|
||||||
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
||||||
if _, from_plugin := post.Props["from_plugin"]; from_plugin {
|
if _, from_plugin := post.Props["from_plugin"]; from_plugin {
|
||||||
return post, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
p.API.CreatePost(&model.Post{
|
p.API.CreatePost(&model.Post{
|
||||||
@@ -57,7 +57,7 @@ func TestPluginDeadlock(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return post, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -120,7 +120,7 @@ func TestPluginDeadlock(t *testing.T) {
|
|||||||
|
|
||||||
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
||||||
if _, from_plugin := post.Props["from_plugin"]; from_plugin {
|
if _, from_plugin := post.Props["from_plugin"]; from_plugin {
|
||||||
return post, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
p.API.CreatePost(&model.Post{
|
p.API.CreatePost(&model.Post{
|
||||||
@@ -132,7 +132,7 @@ func TestPluginDeadlock(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return post, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHookMessageWillBePosted(t *testing.T) {
|
func TestHookMessageWillBePosted(t *testing.T) {
|
||||||
|
t.Run("rejected", func(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
SetAppEnvironmentWithPlugins(t,
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
[]string{
|
|
||||||
`
|
`
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -74,19 +74,144 @@ func TestHookMessageWillBePosted(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
||||||
post.Message = post.Message + "fromplugin"
|
return nil, "rejected"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}, th.App, th.App.NewPluginAPI)
|
||||||
|
|
||||||
|
post := &model.Post{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "message_",
|
||||||
|
CreateAt: model.GetMillis() - 10000,
|
||||||
|
}
|
||||||
|
post, err := th.App.CreatePost(post, th.BasicChannel, false)
|
||||||
|
if assert.NotNil(t, err) {
|
||||||
|
assert.Equal(t, "Post rejected by plugin. rejected", err.Message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("rejected, returned post ignored", func(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
||||||
|
post.Message = "ignored"
|
||||||
|
return post, "rejected"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}, th.App, th.App.NewPluginAPI)
|
||||||
|
|
||||||
|
post := &model.Post{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "message_",
|
||||||
|
CreateAt: model.GetMillis() - 10000,
|
||||||
|
}
|
||||||
|
post, err := th.App.CreatePost(post, th.BasicChannel, false)
|
||||||
|
if assert.NotNil(t, err) {
|
||||||
|
assert.Equal(t, "Post rejected by plugin. rejected", err.Message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("allowed", func(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}, th.App, th.App.NewPluginAPI)
|
||||||
|
|
||||||
|
post := &model.Post{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "message",
|
||||||
|
CreateAt: model.GetMillis() - 10000,
|
||||||
|
}
|
||||||
|
post, err := th.App.CreatePost(post, th.BasicChannel, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, "message", post.Message)
|
||||||
|
if result := <-th.App.Srv.Store.Post().GetSingle(post.Id); result.Err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, "message", result.Data.(*model.Post).Message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("updated", func(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
|
||||||
|
post.Message = post.Message + "_fromplugin"
|
||||||
return post, ""
|
return post, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
plugin.ClientMain(&MyPlugin{})
|
plugin.ClientMain(&MyPlugin{})
|
||||||
}
|
}
|
||||||
`}, th.App, th.App.NewPluginAPI)
|
`,
|
||||||
|
}, th.App, th.App.NewPluginAPI)
|
||||||
|
|
||||||
post := &model.Post{
|
post := &model.Post{
|
||||||
UserId: th.BasicUser.Id,
|
UserId: th.BasicUser.Id,
|
||||||
ChannelId: th.BasicChannel.Id,
|
ChannelId: th.BasicChannel.Id,
|
||||||
Message: "message_",
|
Message: "message",
|
||||||
CreateAt: model.GetMillis() - 10000,
|
CreateAt: model.GetMillis() - 10000,
|
||||||
}
|
}
|
||||||
post, err := th.App.CreatePost(post, th.BasicChannel, false)
|
post, err := th.App.CreatePost(post, th.BasicChannel, false)
|
||||||
@@ -99,14 +224,13 @@ func TestHookMessageWillBePosted(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
assert.Equal(t, "message_fromplugin", result.Data.(*model.Post).Message)
|
assert.Equal(t, "message_fromplugin", result.Data.(*model.Post).Message)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
func TestHookMessageWillBePostedMultiple(t *testing.T) {
|
t.Run("multiple updated", func(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
SetAppEnvironmentWithPlugins(t,
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
[]string{
|
|
||||||
`
|
`
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -163,6 +287,7 @@ func TestHookMessageWillBePostedMultiple(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, "prefix_message_suffix", post.Message)
|
assert.Equal(t, "prefix_message_suffix", post.Message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHookMessageHasBeenPosted(t *testing.T) {
|
func TestHookMessageHasBeenPosted(t *testing.T) {
|
||||||
@@ -171,7 +296,7 @@ func TestHookMessageHasBeenPosted(t *testing.T) {
|
|||||||
|
|
||||||
var mockAPI plugintest.API
|
var mockAPI plugintest.API
|
||||||
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
||||||
mockAPI.On("DeleteUser", "message").Return(nil)
|
mockAPI.On("LogDebug", "message").Return(nil)
|
||||||
|
|
||||||
SetAppEnvironmentWithPlugins(t,
|
SetAppEnvironmentWithPlugins(t,
|
||||||
[]string{
|
[]string{
|
||||||
@@ -188,7 +313,7 @@ func TestHookMessageHasBeenPosted(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *MyPlugin) MessageHasBeenPosted(c *plugin.Context, post *model.Post) {
|
func (p *MyPlugin) MessageHasBeenPosted(c *plugin.Context, post *model.Post) {
|
||||||
p.API.DeleteUser(post.Message)
|
p.API.LogDebug(post.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -261,8 +386,8 @@ func TestHookMessageHasBeenUpdated(t *testing.T) {
|
|||||||
|
|
||||||
var mockAPI plugintest.API
|
var mockAPI plugintest.API
|
||||||
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
||||||
mockAPI.On("DeleteUser", "message_edited").Return(nil)
|
mockAPI.On("LogDebug", "message_edited").Return(nil)
|
||||||
mockAPI.On("DeleteTeam", "message_").Return(nil)
|
mockAPI.On("LogDebug", "message_").Return(nil)
|
||||||
SetAppEnvironmentWithPlugins(t,
|
SetAppEnvironmentWithPlugins(t,
|
||||||
[]string{
|
[]string{
|
||||||
`
|
`
|
||||||
@@ -278,8 +403,8 @@ func TestHookMessageHasBeenUpdated(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *MyPlugin) MessageHasBeenUpdated(c *plugin.Context, newPost, oldPost *model.Post) {
|
func (p *MyPlugin) MessageHasBeenUpdated(c *plugin.Context, newPost, oldPost *model.Post) {
|
||||||
p.API.DeleteUser(newPost.Message)
|
p.API.LogDebug(newPost.Message)
|
||||||
p.API.DeleteTeam(oldPost.Message)
|
p.API.LogDebug(oldPost.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -306,15 +431,167 @@ func TestHookMessageHasBeenUpdated(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHookFileWillBeUploaded(t *testing.T) {
|
func TestHookFileWillBeUploaded(t *testing.T) {
|
||||||
|
t.Run("rejected", func(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
var mockAPI plugintest.API
|
var mockAPI plugintest.API
|
||||||
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
||||||
mockAPI.On("DeleteUser", "testhook.txt").Return(nil)
|
mockAPI.On("LogDebug", "testhook.txt").Return(nil)
|
||||||
mockAPI.On("DeleteTeam", "inputfile").Return(nil)
|
mockAPI.On("LogDebug", "inputfile").Return(nil)
|
||||||
SetAppEnvironmentWithPlugins(t,
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
[]string{
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
|
||||||
|
return nil, "rejected"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
|
||||||
|
|
||||||
|
_, err := th.App.UploadFiles(
|
||||||
|
"noteam",
|
||||||
|
th.BasicChannel.Id,
|
||||||
|
th.BasicUser.Id,
|
||||||
|
[]io.ReadCloser{ioutil.NopCloser(bytes.NewBufferString("inputfile"))},
|
||||||
|
[]string{"testhook.txt"},
|
||||||
|
[]string{},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
if assert.NotNil(t, err) {
|
||||||
|
assert.Equal(t, "File rejected by plugin. rejected", err.Message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("rejected, returned file ignored", func(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
var mockAPI plugintest.API
|
||||||
|
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
||||||
|
mockAPI.On("LogDebug", "testhook.txt").Return(nil)
|
||||||
|
mockAPI.On("LogDebug", "inputfile").Return(nil)
|
||||||
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
|
||||||
|
output.Write([]byte("ignored"))
|
||||||
|
info.Name = "ignored"
|
||||||
|
return info, "rejected"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
|
||||||
|
|
||||||
|
_, err := th.App.UploadFiles(
|
||||||
|
"noteam",
|
||||||
|
th.BasicChannel.Id,
|
||||||
|
th.BasicUser.Id,
|
||||||
|
[]io.ReadCloser{ioutil.NopCloser(bytes.NewBufferString("inputfile"))},
|
||||||
|
[]string{"testhook.txt"},
|
||||||
|
[]string{},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
if assert.NotNil(t, err) {
|
||||||
|
assert.Equal(t, "File rejected by plugin. rejected", err.Message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("allowed", func(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
var mockAPI plugintest.API
|
||||||
|
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
||||||
|
mockAPI.On("LogDebug", "testhook.txt").Return(nil)
|
||||||
|
mockAPI.On("LogDebug", "inputfile").Return(nil)
|
||||||
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
|
||||||
|
|
||||||
|
response, err := th.App.UploadFiles(
|
||||||
|
"noteam",
|
||||||
|
th.BasicChannel.Id,
|
||||||
|
th.BasicUser.Id,
|
||||||
|
[]io.ReadCloser{ioutil.NopCloser(bytes.NewBufferString("inputfile"))},
|
||||||
|
[]string{"testhook.txt"},
|
||||||
|
[]string{},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotNil(t, response)
|
||||||
|
assert.Equal(t, 1, len(response.FileInfos))
|
||||||
|
fileId := response.FileInfos[0].Id
|
||||||
|
|
||||||
|
fileInfo, err := th.App.GetFileInfo(fileId)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotNil(t, fileInfo)
|
||||||
|
assert.Equal(t, "testhook.txt", fileInfo.Name)
|
||||||
|
|
||||||
|
fileReader, err := th.App.FileReader(fileInfo.Path)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
var resultBuf bytes.Buffer
|
||||||
|
io.Copy(&resultBuf, fileReader)
|
||||||
|
assert.Equal(t, "inputfile", resultBuf.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("updated", func(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
var mockAPI plugintest.API
|
||||||
|
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
||||||
|
mockAPI.On("LogDebug", "testhook.txt").Return(nil)
|
||||||
|
mockAPI.On("LogDebug", "inputfile").Return(nil)
|
||||||
|
SetAppEnvironmentWithPlugins(t, []string{
|
||||||
`
|
`
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -330,10 +607,10 @@ func TestHookFileWillBeUploaded(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
|
func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
|
||||||
p.API.DeleteUser(info.Name)
|
p.API.LogDebug(info.Name)
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.ReadFrom(file)
|
buf.ReadFrom(file)
|
||||||
p.API.DeleteTeam(buf.String())
|
p.API.LogDebug(buf.String())
|
||||||
|
|
||||||
outbuf := bytes.NewBufferString("changedtext")
|
outbuf := bytes.NewBufferString("changedtext")
|
||||||
io.Copy(output, outbuf)
|
io.Copy(output, outbuf)
|
||||||
@@ -344,7 +621,8 @@ func TestHookFileWillBeUploaded(t *testing.T) {
|
|||||||
func main() {
|
func main() {
|
||||||
plugin.ClientMain(&MyPlugin{})
|
plugin.ClientMain(&MyPlugin{})
|
||||||
}
|
}
|
||||||
`}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
|
`,
|
||||||
|
}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
|
||||||
|
|
||||||
response, err := th.App.UploadFiles(
|
response, err := th.App.UploadFiles(
|
||||||
"noteam",
|
"noteam",
|
||||||
@@ -370,6 +648,7 @@ func TestHookFileWillBeUploaded(t *testing.T) {
|
|||||||
var resultBuf bytes.Buffer
|
var resultBuf bytes.Buffer
|
||||||
io.Copy(&resultBuf, fileReader)
|
io.Copy(&resultBuf, fileReader)
|
||||||
assert.Equal(t, "changedtext", resultBuf.String())
|
assert.Equal(t, "changedtext", resultBuf.String())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserWillLogIn_Blocked(t *testing.T) {
|
func TestUserWillLogIn_Blocked(t *testing.T) {
|
||||||
|
|||||||
18
app/post.go
18
app/post.go
@@ -162,14 +162,22 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if a.PluginsReady() {
|
if a.PluginsReady() {
|
||||||
var rejectionReason string
|
var rejectionError *model.AppError
|
||||||
pluginContext := &plugin.Context{}
|
pluginContext := &plugin.Context{}
|
||||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||||
post, rejectionReason = hooks.MessageWillBePosted(pluginContext, post)
|
replacementPost, rejectionReason := hooks.MessageWillBePosted(pluginContext, post)
|
||||||
return post != nil
|
if rejectionReason != "" {
|
||||||
|
rejectionError = model.NewAppError("createPost", "Post rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if replacementPost != nil {
|
||||||
|
post = replacementPost
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}, plugin.MessageWillBePostedId)
|
}, plugin.MessageWillBePostedId)
|
||||||
if post == nil {
|
if rejectionError != nil {
|
||||||
return nil, model.NewAppError("createPost", "Post rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest)
|
return nil, rejectionError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,10 @@ type Hooks interface {
|
|||||||
|
|
||||||
// MessageWillBePosted is invoked when a message is posted by a user before it is committed
|
// MessageWillBePosted is invoked when a message is posted by a user before it is committed
|
||||||
// to the database. If you also want to act on edited posts, see MessageWillBeUpdated.
|
// to the database. If you also want to act on edited posts, see MessageWillBeUpdated.
|
||||||
// Return values should be the modified post or nil if rejected and an explanation for the user.
|
//
|
||||||
|
// To reject a post, return an non-empty string describing why the post was rejected.
|
||||||
|
// To modify the post, return the replacement, non-nil *model.Post and an empty string.
|
||||||
|
// To allow the post without modification, return a nil *model.Post and an empty string.
|
||||||
//
|
//
|
||||||
// If you don't need to modify or reject posts, use MessageHasBeenPosted instead.
|
// If you don't need to modify or reject posts, use MessageHasBeenPosted instead.
|
||||||
//
|
//
|
||||||
@@ -129,8 +132,12 @@ type Hooks interface {
|
|||||||
UserHasLoggedIn(c *Context, user *model.User)
|
UserHasLoggedIn(c *Context, user *model.User)
|
||||||
|
|
||||||
// FileWillBeUploaded is invoked when a file is uploaded, but before it is committed to backing store.
|
// FileWillBeUploaded is invoked when a file is uploaded, but before it is committed to backing store.
|
||||||
// Read from file to retrieve the body of the uploaded file. You may modify the body of the file by writing to output.
|
// Read from file to retrieve the body of the uploaded file.
|
||||||
// Returned FileInfo will be used instead of input FileInfo. Return nil to reject the file upload and include a text reason as the second argument.
|
//
|
||||||
|
// To reject a file upload, return an non-empty string describing why the file was rejected.
|
||||||
|
// To modify the file, write to the output and/or return a non-nil *model.FileInfo, as well as an empty string.
|
||||||
|
// To allow the file without modification, do not write to the output and return a nil *model.FileInfo and an empty string.
|
||||||
|
//
|
||||||
// Note that this method will be called for files uploaded by plugins, including the plugin that uploaded the post.
|
// Note that this method will be called for files uploaded by plugins, including the plugin that uploaded the post.
|
||||||
// FileInfo.Size will be automatically set properly if you modify the file.
|
// FileInfo.Size will be automatically set properly if you modify the file.
|
||||||
FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string)
|
FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string)
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user