MM-14575 - Automatically serve static files for plugins (#10476)
* MM-14575 - Automatically serve static files for plugins * Added static handler for plugin public files * Added StaticFilesPath method to Environment for use by MainRouter * Added "static_files" property to Manifest Server * Added unit tests for these changes * MM-14575: Adding comment for cache control value * MM-14575: Moved Static Plugin Request handler to plugin_requests * Updated testing * MM-14575: Removing the StaticFiles from Manifest Server * MM-14575: Removing static files from test * MM-14575: Updating static files test * MM14575: Removing cache directive from plugin static files * MM14575: Moving plugin public directory to root * MM-14575: Updating tests for changed public directory * MM-14575: Moved compileGo to a common utils package for tests * MM-14575: Moving plugins initialization to InitPlugins find in tests * Update utils/test_files_compiler.go Adding Copyright header Co-Authored-By: happygaijin <happygaijin@users.noreply.github.com> * MM-14575: Consistent usage of static vs public name * Removing spurious newline * Comment typo Co-Authored-By: happygaijin <happygaijin@users.noreply.github.com> * Removing spurious new line Co-Authored-By: happygaijin <happygaijin@users.noreply.github.com> * MM14575: Adding a test to make sure only public files can be requested * MM-14575 Adding a test for redirects on public files
Этот коммит содержится в:
коммит произвёл
Jesse Hallam
родитель
7c9837d9b1
Коммит
ba34b4607c
@@ -8,7 +8,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mattermost/mattermost-server/utils/fileutils"
|
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/png"
|
"image/png"
|
||||||
@@ -21,13 +20,16 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/utils/fileutils"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/plugin"
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, pluginId string, app *App) {
|
func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, pluginId string, app *App) string {
|
||||||
pluginDir, err := ioutil.TempDir("", "")
|
pluginDir, err := ioutil.TempDir("", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
webappPluginDir, err := ioutil.TempDir("", "")
|
webappPluginDir, err := ioutil.TempDir("", "")
|
||||||
@@ -39,7 +41,7 @@ func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string,
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
backend := filepath.Join(pluginDir, pluginId, "backend.exe")
|
backend := filepath.Join(pluginDir, pluginId, "backend.exe")
|
||||||
compileGo(t, pluginCode, backend)
|
utils.CompileGo(t, pluginCode, backend)
|
||||||
|
|
||||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifest), 0600)
|
ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifest), 0600)
|
||||||
manifest, activated, reterr := env.Activate(pluginId)
|
manifest, activated, reterr := env.Activate(pluginId)
|
||||||
@@ -48,8 +50,39 @@ func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string,
|
|||||||
require.True(t, activated)
|
require.True(t, activated)
|
||||||
|
|
||||||
app.SetPluginsEnvironment(env)
|
app.SetPluginsEnvironment(env)
|
||||||
|
|
||||||
|
return pluginDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPublicFilesPathConfiguration(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
pluginID := "com.mattermost.sample"
|
||||||
|
|
||||||
|
pluginDir := setupPluginApiTest(t,
|
||||||
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`{"id": "com.mattermost.sample", "server": {"executable": "backend.exe"}, "settings_schema": {"settings": []}}`, pluginID, th.App)
|
||||||
|
|
||||||
|
publicFilesFolderInTest := filepath.Join(pluginDir, pluginID, "public")
|
||||||
|
publicFilesPath, err := th.App.GetPluginsEnvironment().PublicFilesPath(pluginID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, publicFilesPath, publicFilesFolderInTest)
|
||||||
|
}
|
||||||
func TestPluginAPIGetUsers(t *testing.T) {
|
func TestPluginAPIGetUsers(t *testing.T) {
|
||||||
th := Setup(t)
|
th := Setup(t)
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -460,7 +493,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
|
|||||||
var pluginManifests []*model.Manifest
|
var pluginManifests []*model.Manifest
|
||||||
for _, pluginID := range pluginIDs {
|
for _, pluginID := range pluginIDs {
|
||||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||||
compileGo(t, pluginCode, backend)
|
utils.CompileGo(t, pluginCode, backend)
|
||||||
|
|
||||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
|
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
|
||||||
manifest, activated, reterr := env.Activate(pluginID)
|
manifest, activated, reterr := env.Activate(pluginID)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -22,22 +21,11 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/plugin"
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
"github.com/mattermost/mattermost-server/plugin/plugintest"
|
"github.com/mattermost/mattermost-server/plugin/plugintest"
|
||||||
"github.com/mattermost/mattermost-server/plugin/plugintest/mock"
|
"github.com/mattermost/mattermost-server/plugin/plugintest/mock"
|
||||||
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func compileGo(t *testing.T, sourceCode, outputPath string) {
|
|
||||||
dir, err := ioutil.TempDir(".", "")
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte(sourceCode), 0600))
|
|
||||||
cmd := exec.Command("go", "build", "-o", outputPath, "main.go")
|
|
||||||
cmd.Dir = dir
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
require.NoError(t, cmd.Run(), "failed to compile go")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, apiFunc func(*model.Manifest) plugin.API) (func(), []string, []error) {
|
func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, apiFunc func(*model.Manifest) plugin.API) (func(), []string, []error) {
|
||||||
pluginDir, err := ioutil.TempDir("", "")
|
pluginDir, err := ioutil.TempDir("", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -53,7 +41,7 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
|
|||||||
for _, code := range pluginCode {
|
for _, code := range pluginCode {
|
||||||
pluginId := model.NewId()
|
pluginId := model.NewId()
|
||||||
backend := filepath.Join(pluginDir, pluginId, "backend.exe")
|
backend := filepath.Join(pluginDir, pluginId, "backend.exe")
|
||||||
compileGo(t, code, backend)
|
utils.CompileGo(t, code, backend)
|
||||||
|
|
||||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(`{"id": "`+pluginId+`", "backend": {"executable": "backend.exe"}}`), 0600)
|
ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(`{"id": "`+pluginId+`", "backend": {"executable": "backend.exe"}}`), 0600)
|
||||||
_, _, activationErr := env.Activate(pluginId)
|
_, _, activationErr := env.Activate(pluginId)
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -40,6 +42,34 @@ func (a *App) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.servePluginRequest(w, r, hooks.ServeHTTP)
|
a.servePluginRequest(w, r, hooks.ServeHTTP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServePluginPublicRequest serves public plugin files
|
||||||
|
// at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything}
|
||||||
|
func (a *App) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be in the form of /$PLUGIN_ID/public/{anything} by the time we get here
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
pluginID := vars["plugin_id"]
|
||||||
|
|
||||||
|
publicFilesPath, err := a.GetPluginsEnvironment().PublicFilesPath(pluginID)
|
||||||
|
if err != nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
publicFilePath := path.Clean(r.URL.Path)
|
||||||
|
prefix := fmt.Sprintf("/plugins/%s/public/", pluginID)
|
||||||
|
if !strings.HasPrefix(publicFilePath, prefix) {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
publicFile := filepath.Join(publicFilesPath, strings.TrimPrefix(publicFilePath, prefix))
|
||||||
|
http.ServeFile(w, r, publicFile)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) {
|
func (a *App) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) {
|
||||||
token := ""
|
token := ""
|
||||||
context := &plugin.Context{
|
context := &plugin.Context{
|
||||||
|
|||||||
@@ -100,8 +100,10 @@ func (s *Server) RunOldAppInitalization() error {
|
|||||||
return errors.Wrap(err, "failed to parse SiteURL subpath")
|
return errors.Wrap(err, "failed to parse SiteURL subpath")
|
||||||
}
|
}
|
||||||
s.FakeApp().Srv.Router = s.FakeApp().Srv.RootRouter.PathPrefix(subpath).Subrouter()
|
s.FakeApp().Srv.Router = s.FakeApp().Srv.RootRouter.PathPrefix(subpath).Subrouter()
|
||||||
s.FakeApp().Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}", s.FakeApp().ServePluginRequest)
|
pluginsRoute := s.FakeApp().Srv.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
|
||||||
s.FakeApp().Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", s.FakeApp().ServePluginRequest)
|
pluginsRoute.HandleFunc("", s.FakeApp().ServePluginRequest)
|
||||||
|
pluginsRoute.HandleFunc("/public/{public_file:.*}", s.FakeApp().ServePluginPublicRequest)
|
||||||
|
pluginsRoute.HandleFunc("/{anything:.*}", s.FakeApp().ServePluginRequest)
|
||||||
|
|
||||||
// If configured with a subpath, redirect 404s at the root back into the subpath.
|
// If configured with a subpath, redirect 404s at the root back into the subpath.
|
||||||
if subpath != "/" {
|
if subpath != "/" {
|
||||||
|
|||||||
@@ -94,6 +94,15 @@ func (env *Environment) IsActive(id string) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicFilesPath returns a path and true if the plugin with the given id is active.
|
||||||
|
// It returns an empty string and false if the path is not set or invalid
|
||||||
|
func (env *Environment) PublicFilesPath(id string) (string, error) {
|
||||||
|
if _, ok := env.activePlugins.Load(id); !ok {
|
||||||
|
return "", fmt.Errorf("plugin not found: %v", id)
|
||||||
|
}
|
||||||
|
return filepath.Join(env.pluginDir, id, "public"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Statuses returns a list of plugin statuses representing the state of every plugin
|
// Statuses returns a list of plugin statuses representing the state of every plugin
|
||||||
func (env *Environment) Statuses() (model.PluginStatuses, error) {
|
func (env *Environment) Statuses() (model.PluginStatuses, error) {
|
||||||
plugins, err := env.Available()
|
plugins, err := env.Available()
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ package plugin
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -26,18 +26,6 @@ func TestSupervisor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileGo(t *testing.T, sourceCode, outputPath string) {
|
|
||||||
dir, err := ioutil.TempDir(".", "")
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte(sourceCode), 0600))
|
|
||||||
cmd := exec.Command("go", "build", "-o", outputPath, "main.go")
|
|
||||||
cmd.Dir = dir
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
require.NoError(t, cmd.Run())
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSupervisor_InvalidExecutablePath(t *testing.T) {
|
func testSupervisor_InvalidExecutablePath(t *testing.T) {
|
||||||
dir, err := ioutil.TempDir("", "")
|
dir, err := ioutil.TempDir("", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -83,7 +71,7 @@ func testSupervisor_StartTimeout(t *testing.T) {
|
|||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
backend := filepath.Join(dir, "backend.exe")
|
backend := filepath.Join(dir, "backend.exe")
|
||||||
compileGo(t, `
|
utils.CompileGo(t, `
|
||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
26
utils/test_files_compiler.go
Обычный файл
26
utils/test_files_compiler.go
Обычный файл
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CompileGo(t *testing.T, sourceCode, outputPath string) {
|
||||||
|
dir, err := ioutil.TempDir(".", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte(sourceCode), 0600))
|
||||||
|
cmd := exec.Command("go", "build", "-o", outputPath, "main.go")
|
||||||
|
cmd.Dir = dir
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
require.NoError(t, cmd.Run(), "failed to compile go")
|
||||||
|
}
|
||||||
107
web/web_test.go
107
web/web_test.go
@@ -5,11 +5,22 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/testlib"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/app"
|
"github.com/mattermost/mattermost-server/app"
|
||||||
"github.com/mattermost/mattermost-server/config"
|
"github.com/mattermost/mattermost-server/config"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ApiClient *model.Client4
|
var ApiClient *model.Client4
|
||||||
@@ -18,12 +29,15 @@ var URL string
|
|||||||
type TestHelper struct {
|
type TestHelper struct {
|
||||||
App *app.App
|
App *app.App
|
||||||
Server *app.Server
|
Server *app.Server
|
||||||
|
Web *Web
|
||||||
|
|
||||||
BasicUser *model.User
|
BasicUser *model.User
|
||||||
BasicChannel *model.Channel
|
BasicChannel *model.Channel
|
||||||
BasicTeam *model.Team
|
BasicTeam *model.Team
|
||||||
|
|
||||||
SystemAdminUser *model.User
|
SystemAdminUser *model.User
|
||||||
|
|
||||||
|
tempWorkspace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Setup() *TestHelper {
|
func Setup() *TestHelper {
|
||||||
@@ -52,7 +66,7 @@ func Setup() *TestHelper {
|
|||||||
}
|
}
|
||||||
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||||
|
|
||||||
New(s, s.AppOptions, s.Router)
|
web := New(s, s.AppOptions, s.Router)
|
||||||
URL = fmt.Sprintf("http://localhost:%v", a.Srv.ListenAddr.Port)
|
URL = fmt.Sprintf("http://localhost:%v", a.Srv.ListenAddr.Port)
|
||||||
ApiClient = model.NewAPIv4Client(URL)
|
ApiClient = model.NewAPIv4Client(URL)
|
||||||
|
|
||||||
@@ -68,11 +82,26 @@ func Setup() *TestHelper {
|
|||||||
th := &TestHelper{
|
th := &TestHelper{
|
||||||
App: a,
|
App: a,
|
||||||
Server: s,
|
Server: s,
|
||||||
|
Web: web,
|
||||||
}
|
}
|
||||||
|
|
||||||
return th
|
return th
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (th *TestHelper) InitPlugins() *TestHelper {
|
||||||
|
|
||||||
|
if th.tempWorkspace == "" {
|
||||||
|
th.tempWorkspace, _ = testlib.SetupTestResources()
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginDir := filepath.Join(th.tempWorkspace, "plugins")
|
||||||
|
webappDir := filepath.Join(th.tempWorkspace, "webapp")
|
||||||
|
|
||||||
|
th.App.InitPlugins(pluginDir, webappDir)
|
||||||
|
|
||||||
|
return th
|
||||||
|
}
|
||||||
|
|
||||||
func (th *TestHelper) InitBasic() *TestHelper {
|
func (th *TestHelper) InitBasic() *TestHelper {
|
||||||
th.SystemAdminUser, _ = th.App.CreateUser(&model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", EmailVerified: true, Roles: model.SYSTEM_ADMIN_ROLE_ID})
|
th.SystemAdminUser, _ = th.App.CreateUser(&model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", EmailVerified: true, Roles: model.SYSTEM_ADMIN_ROLE_ID})
|
||||||
|
|
||||||
@@ -98,6 +127,82 @@ func (th *TestHelper) TearDown() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPublicFilesRequest(t *testing.T) {
|
||||||
|
th := Setup().InitPlugins()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
pluginDir, err := ioutil.TempDir("", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
webappPluginDir, err := ioutil.TempDir("", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(pluginDir)
|
||||||
|
defer os.RemoveAll(webappPluginDir)
|
||||||
|
|
||||||
|
env, err := plugin.NewEnvironment(th.App.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pluginID := "com.mattermost.sample"
|
||||||
|
pluginCode :=
|
||||||
|
`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
// Compile and write the plugin
|
||||||
|
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||||
|
utils.CompileGo(t, pluginCode, backend)
|
||||||
|
|
||||||
|
// Write the plugin.json manifest
|
||||||
|
pluginManifest := `{"id": "com.mattermost.sample", "server": {"executable": "backend.exe"}, "settings_schema": {"settings": []}}`
|
||||||
|
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifest), 0600)
|
||||||
|
|
||||||
|
// Write the test public file
|
||||||
|
helloHTML := `Hello from the static files public folder for the com.mattermost.sample plugin!`
|
||||||
|
htmlFolderPath := filepath.Join(pluginDir, pluginID, "public")
|
||||||
|
os.MkdirAll(htmlFolderPath, os.ModePerm)
|
||||||
|
htmlFilePath := filepath.Join(htmlFolderPath, "hello.html")
|
||||||
|
|
||||||
|
htmlFileErr := ioutil.WriteFile(htmlFilePath, []byte(helloHTML), 0600)
|
||||||
|
assert.NoError(t, htmlFileErr)
|
||||||
|
|
||||||
|
nefariousHTML := `You shouldn't be able to get here!`
|
||||||
|
htmlFileErr = ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "nefarious-file-access.html"), []byte(nefariousHTML), 0600)
|
||||||
|
assert.NoError(t, htmlFileErr)
|
||||||
|
|
||||||
|
manifest, activated, reterr := env.Activate(pluginID)
|
||||||
|
require.Nil(t, reterr)
|
||||||
|
require.NotNil(t, manifest)
|
||||||
|
require.True(t, activated)
|
||||||
|
|
||||||
|
th.App.SetPluginsEnvironment(env)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", "/plugins/com.mattermost.sample/public/hello.html", nil)
|
||||||
|
res := httptest.NewRecorder()
|
||||||
|
th.Web.MainRouter.ServeHTTP(res, req)
|
||||||
|
assert.Equal(t, helloHTML, res.Body.String())
|
||||||
|
|
||||||
|
req, _ = http.NewRequest("GET", "/plugins/com.mattermost.sample/nefarious-file-access.html", nil)
|
||||||
|
res = httptest.NewRecorder()
|
||||||
|
th.Web.MainRouter.ServeHTTP(res, req)
|
||||||
|
assert.Equal(t, 404, res.Code)
|
||||||
|
|
||||||
|
req, _ = http.NewRequest("GET", "/plugins/com.mattermost.sample/public/../nefarious-file-access.html", nil)
|
||||||
|
res = httptest.NewRecorder()
|
||||||
|
th.Web.MainRouter.ServeHTTP(res, req)
|
||||||
|
assert.Equal(t, 301, res.Code)
|
||||||
|
}
|
||||||
|
|
||||||
/* Test disabled for now so we don't requrie the client to build. Maybe re-enable after client gets moved out.
|
/* Test disabled for now so we don't requrie the client to build. Maybe re-enable after client gets moved out.
|
||||||
func TestStatic(t *testing.T) {
|
func TestStatic(t *testing.T) {
|
||||||
Setup()
|
Setup()
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user