2017-04-12 08:27:57 -04:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2019-11-29 12:59:40 +01:00
// See LICENSE.txt for license information.
2015-06-14 23:53:32 -08:00
package web
import (
2017-10-16 08:09:43 -07:00
"fmt"
2019-04-05 07:35:51 -07:00
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
2015-06-14 23:53:32 -08:00
"testing"
2019-12-05 23:57:30 -04:00
"time"
2015-10-15 11:16:26 -07:00
2019-11-28 14:39:38 +01:00
"github.com/mattermost/mattermost-server/v5/app"
"github.com/mattermost/mattermost-server/v5/config"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/plugin"
2020-03-02 17:13:39 +01:00
"github.com/mattermost/mattermost-server/v5/store"
2020-03-04 14:18:03 +01:00
"github.com/mattermost/mattermost-server/v5/store/localcachelayer"
2020-03-02 17:13:39 +01:00
"github.com/mattermost/mattermost-server/v5/store/storetest/mocks"
"github.com/mattermost/mattermost-server/v5/testlib"
2019-11-28 14:39:38 +01:00
"github.com/mattermost/mattermost-server/v5/utils"
2019-04-05 07:35:51 -07:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2015-06-14 23:53:32 -08:00
)
2018-05-16 13:43:22 -04:00
var ApiClient * model . Client4
2015-06-14 23:53:32 -08:00
var URL string
2018-05-14 10:24:58 -04:00
type TestHelper struct {
2020-02-13 13:26:58 +01:00
App app . AppIface
2018-11-28 10:56:21 -08:00
Server * app . Server
2019-04-05 07:35:51 -07:00
Web * Web
2018-07-06 09:07:36 +01:00
2018-09-26 20:49:22 +00:00
BasicUser * model . User
BasicChannel * model . Channel
BasicTeam * model . Team
2018-07-06 09:07:36 +01:00
2018-09-26 20:49:22 +00:00
SystemAdminUser * model . User
2019-04-05 07:35:51 -07:00
tempWorkspace string
2020-03-04 14:18:03 +01:00
IncludeCacheLayer bool
2018-05-14 10:24:58 -04:00
}
2020-03-02 17:13:39 +01:00
func SetupWithStoreMock ( tb testing . TB ) * TestHelper {
if testing . Short ( ) {
tb . SkipNow ( )
}
store := testlib . GetMockStoreForSetupFunctions ( )
2020-03-04 14:18:03 +01:00
th := setupTestHelper ( tb , store , false )
2020-03-02 17:13:39 +01:00
emptyMockStore := mocks . Store { }
emptyMockStore . On ( "Close" ) . Return ( nil )
th . App . Srv ( ) . Store = & emptyMockStore
return th
}
2020-02-10 19:31:41 +01:00
func Setup ( tb testing . TB ) * TestHelper {
2020-03-02 17:13:39 +01:00
if testing . Short ( ) {
tb . SkipNow ( )
}
2019-02-19 19:50:11 +05:30
store := mainHelper . GetStore ( )
store . DropAllTables ( )
2020-03-04 14:18:03 +01:00
return setupTestHelper ( tb , store , true )
2020-03-02 17:13:39 +01:00
}
2018-12-06 13:19:32 -05:00
2020-03-04 14:18:03 +01:00
func setupTestHelper ( t testing . TB , store store . Store , includeCacheLayer bool ) * TestHelper {
2019-08-09 11:33:59 -04:00
memoryStore , err := config . NewMemoryStoreWithOptions ( & config . MemoryStoreOptions { IgnoreEnvironmentOverrides : true } )
2019-02-12 08:37:54 -05:00
if err != nil {
2019-03-06 15:06:45 -05:00
panic ( "failed to initialize memory store: " + err . Error ( ) )
2019-02-12 08:37:54 -05:00
}
2019-03-06 15:06:45 -05:00
var options [ ] app . Option
options = append ( options , app . ConfigStore ( memoryStore ) )
options = append ( options , app . StoreOverride ( mainHelper . Store ) )
2019-02-12 08:37:54 -05:00
s , err := app . NewServer ( options ... )
2018-01-11 15:23:41 -06:00
if err != nil {
panic ( err )
}
2020-03-04 14:18:03 +01:00
if includeCacheLayer {
// Adds the cache layer to the test store
s . Store = localcachelayer . NewLocalCacheLayer ( s . Store , s . Metrics , s . Cluster , s . CacheProvider )
}
2018-11-28 10:56:21 -08:00
a := s . FakeApp ( )
2017-10-18 15:36:43 -07:00
prevListenAddress := * a . Config ( ) . ServiceSettings . ListenAddress
a . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . ListenAddress = ":0" } )
2018-12-17 08:51:46 -08:00
serverErr := s . Start ( )
2018-02-07 13:41:15 +05:30
if serverErr != nil {
panic ( serverErr )
}
2017-10-18 15:36:43 -07:00
a . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . ListenAddress = prevListenAddress } )
2018-05-14 10:24:58 -04:00
2019-05-21 20:03:36 +02:00
// Disable strict password requirements for test
a . UpdateConfig ( func ( cfg * model . Config ) {
* cfg . PasswordSettings . MinimumLength = 5
* cfg . PasswordSettings . Lowercase = false
* cfg . PasswordSettings . Uppercase = false
* cfg . PasswordSettings . Symbol = false
* cfg . PasswordSettings . Number = false
} )
2019-04-05 07:35:51 -07:00
web := New ( s , s . AppOptions , s . Router )
2020-02-13 13:26:58 +01:00
URL = fmt . Sprintf ( "http://localhost:%v" , a . Srv ( ) . ListenAddr . Port )
2018-05-16 13:43:22 -04:00
ApiClient = model . NewAPIv4Client ( URL )
2017-10-09 14:59:48 -07:00
2019-04-30 20:36:21 +02:00
a . DoAppMigrations ( )
2018-02-06 15:34:08 +00:00
2020-02-13 13:26:58 +01:00
a . Srv ( ) . Store . MarkSystemRanUnitTests ( )
2017-10-09 14:59:48 -07:00
2017-10-31 09:39:31 -05:00
a . UpdateConfig ( func ( cfg * model . Config ) {
* cfg . TeamSettings . EnableOpenServer = true
} )
2017-10-09 14:59:48 -07:00
2018-05-14 10:24:58 -04:00
th := & TestHelper {
2020-03-04 14:18:03 +01:00
App : a ,
Server : s ,
Web : web ,
IncludeCacheLayer : includeCacheLayer ,
2019-04-05 07:35:51 -07:00
}
return th
}
func ( th * TestHelper ) InitPlugins ( ) * TestHelper {
pluginDir := filepath . Join ( th . tempWorkspace , "plugins" )
webappDir := filepath . Join ( th . tempWorkspace , "webapp" )
th . App . InitPlugins ( pluginDir , webappDir )
2018-05-14 10:24:58 -04:00
return th
}
func ( th * TestHelper ) InitBasic ( ) * TestHelper {
2018-07-06 09:07:36 +01:00
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 } )
user , _ := th . App . CreateUser ( & model . User { Email : model . NewId ( ) + "success+test@simulator.amazonses.com" , Nickname : "Corey Hulen" , Password : "passwd1" , EmailVerified : true , Roles : model . SYSTEM_USER_ROLE_ID } )
2018-05-14 10:24:58 -04:00
team , _ := th . App . CreateTeam ( & model . Team { DisplayName : "Name" , Name : "z-z-" + model . NewId ( ) + "a" , Email : user . Email , Type : model . TEAM_OPEN } )
th . App . JoinUserToTeam ( team , user , "" )
channel , _ := th . App . CreateChannel ( & model . Channel { DisplayName : "Test API Name" , Name : "zz" + model . NewId ( ) + "a" , Type : model . CHANNEL_OPEN , TeamId : team . Id , CreatorId : user . Id } , true )
th . BasicUser = user
th . BasicChannel = channel
th . BasicTeam = team
return th
2015-06-14 23:53:32 -08:00
}
2018-05-14 10:24:58 -04:00
func ( th * TestHelper ) TearDown ( ) {
2020-03-04 14:18:03 +01:00
if th . IncludeCacheLayer {
// Clean all the caches
th . App . InvalidateAllCaches ( )
}
2018-11-28 10:56:21 -08:00
th . Server . Shutdown ( )
2015-06-14 23:53:32 -08:00
}
2019-12-05 23:57:30 -04:00
func TestStaticFilesRequest ( t * testing . T ) {
2020-02-10 19:31:41 +01:00
th := Setup ( t ) . InitPlugins ( )
2019-12-05 23:57:30 -04:00
defer th . TearDown ( )
pluginID := "com.mattermost.sample"
// Setup the directory directly in the plugin working path.
pluginDir := filepath . Join ( * th . App . Config ( ) . PluginSettings . Directory , pluginID )
err := os . MkdirAll ( pluginDir , 0777 )
require . NoError ( t , err )
pluginDir , err = filepath . Abs ( pluginDir )
require . NoError ( t , err )
// Compile the backend
backend := filepath . Join ( pluginDir , "backend.exe" )
pluginCode := `
package main
import (
"github.com/mattermost/mattermost-server/v5/plugin"
)
type MyPlugin struct {
plugin . MattermostPlugin
}
func main ( ) {
plugin . ClientMain ( & MyPlugin { } )
}
`
utils . CompileGo ( t , pluginCode , backend )
// Write out the frontend
mainJS := ` var x = alert(); `
mainJSPath := filepath . Join ( pluginDir , "main.js" )
require . NoError ( t , err )
err = ioutil . WriteFile ( mainJSPath , [ ] byte ( mainJS ) , 0777 )
require . NoError ( t , err )
// Write the plugin.json manifest
pluginManifest := ` { "id": "com.mattermost.sample", "server": { "executable": "backend.exe"}, "webapp": { "bundle_path":"main.js"}, "settings_schema": { "settings": []}} `
ioutil . WriteFile ( filepath . Join ( pluginDir , "plugin.json" ) , [ ] byte ( pluginManifest ) , 0600 )
// Activate the plugin
manifest , activated , reterr := th . App . GetPluginsEnvironment ( ) . Activate ( pluginID )
require . Nil ( t , reterr )
require . NotNil ( t , manifest )
require . True ( t , activated )
// Verify access to the bundle with requisite headers
req , _ := http . NewRequest ( "GET" , "/static/plugins/com.mattermost.sample/com.mattermost.sample_724ed0e2ebb2b841_bundle.js" , nil )
res := httptest . NewRecorder ( )
th . Web . MainRouter . ServeHTTP ( res , req )
assert . Equal ( t , http . StatusOK , res . Code )
assert . Equal ( t , mainJS , res . Body . String ( ) )
2020-01-08 14:43:59 -06:00
assert . Equal ( t , [ ] string { "max-age=31556926, public" } , res . Result ( ) . Header [ http . CanonicalHeaderKey ( "Cache-Control" ) ] )
2019-12-05 23:57:30 -04:00
// Verify cached access to the bundle with an If-Modified-Since timestamp in the future
future := time . Now ( ) . Add ( 24 * time . Hour )
req , _ = http . NewRequest ( "GET" , "/static/plugins/com.mattermost.sample/com.mattermost.sample_724ed0e2ebb2b841_bundle.js" , nil )
req . Header . Add ( "If-Modified-Since" , future . Format ( time . RFC850 ) )
res = httptest . NewRecorder ( )
th . Web . MainRouter . ServeHTTP ( res , req )
assert . Equal ( t , http . StatusNotModified , res . Code )
assert . Empty ( t , res . Body . String ( ) )
2020-01-08 14:43:59 -06:00
assert . Equal ( t , [ ] string { "max-age=31556926, public" } , res . Result ( ) . Header [ http . CanonicalHeaderKey ( "Cache-Control" ) ] )
2019-12-05 23:57:30 -04:00
// Verify access to the bundle with an If-Modified-Since timestamp in the past
past := time . Now ( ) . Add ( - 24 * time . Hour )
req , _ = http . NewRequest ( "GET" , "/static/plugins/com.mattermost.sample/com.mattermost.sample_724ed0e2ebb2b841_bundle.js" , nil )
req . Header . Add ( "If-Modified-Since" , past . Format ( time . RFC850 ) )
res = httptest . NewRecorder ( )
th . Web . MainRouter . ServeHTTP ( res , req )
assert . Equal ( t , http . StatusOK , res . Code )
assert . Equal ( t , mainJS , res . Body . String ( ) )
2020-01-08 14:43:59 -06:00
assert . Equal ( t , [ ] string { "max-age=31556926, public" } , res . Result ( ) . Header [ http . CanonicalHeaderKey ( "Cache-Control" ) ] )
2019-12-05 23:57:30 -04:00
// Verify handling of 404.
req , _ = http . NewRequest ( "GET" , "/static/plugins/com.mattermost.sample/404.js" , nil )
res = httptest . NewRecorder ( )
th . Web . MainRouter . ServeHTTP ( res , req )
assert . Equal ( t , http . StatusNotFound , res . Code )
assert . Equal ( t , "404 page not found\n" , res . Body . String ( ) )
assert . Equal ( t , [ ] string { "no-cache, public" } , res . Result ( ) . Header [ http . CanonicalHeaderKey ( "Cache-Control" ) ] )
}
2019-04-05 07:35:51 -07:00
func TestPublicFilesRequest ( t * testing . T ) {
2020-02-10 19:31:41 +01:00
th := Setup ( t ) . InitPlugins ( )
2019-04-05 07:35:51 -07:00
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 )
2020-02-14 15:47:43 -05:00
env , err := plugin . NewEnvironment ( th . App . NewPluginAPI , pluginDir , webappPluginDir , th . App . Log ( ) , nil )
2019-04-05 07:35:51 -07:00
require . NoError ( t , err )
pluginID := "com.mattermost.sample"
pluginCode :=
`
package main
import (
2019-11-28 14:39:38 +01:00
"github.com/mattermost/mattermost-server/v5/plugin"
2019-04-05 07:35:51 -07:00
)
type MyPlugin struct {
plugin . MattermostPlugin
}
func main ( ) {
plugin . ClientMain ( & MyPlugin { } )
}
2019-10-21 17:00:21 +02:00
2019-04-05 07:35:51 -07:00
`
// 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 )
}
2016-03-22 11:35:39 -04:00
/ * Test disabled for now so we don ' t requrie the client to build . Maybe re - enable after client gets moved out .
2015-06-14 23:53:32 -08:00
func TestStatic ( t * testing . T ) {
Setup ( )
2015-09-11 10:43:33 -04:00
// add a short delay to make sure the server is ready to receive requests
time . Sleep ( 1 * time . Second )
2015-06-14 23:53:32 -08:00
2016-03-14 08:50:46 -04:00
resp , err := http . Get ( URL + "/static/root.html" )
2015-09-11 10:43:33 -04:00
2019-10-21 17:00:21 +02:00
assert . NoErrorf ( t , err , "got error while trying to get static files %v" , err )
assert . Equalf ( t , resp . StatusCode , http . StatusOK , "couldn't get static files %v" , resp . StatusCode )
2015-06-14 23:53:32 -08:00
}
2016-03-22 11:35:39 -04:00
* /
2015-06-14 23:53:32 -08:00
2018-03-06 13:22:07 -08:00
func TestCheckClientCompatability ( t * testing . T ) {
//Browser Name, UA String, expected result (if the browser should fail the test false and if it should pass the true)
type uaTest struct {
Name string // Name of Browser
UserAgent string // Useragent of Browser
Result bool // Expected result (true if browser should be compatible, false if browser shouldn't be compatible)
}
var uaTestParameters = [ ] uaTest {
{ "Mozilla 40.1" , "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1" , true } ,
{ "Chrome 60" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36" , true } ,
{ "Chrome Mobile" , "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Mobile Safari/537.36" , true } ,
{ "MM Classic App" , "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR6.170623.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.81 Mobile Safari/537.36 Web-Atoms-Mobile-WebView" , true } ,
{ "MM App 3.7.1" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Mattermost/3.7.1 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36" , true } ,
{ "Franz 4.0.4" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Franz/4.0.4 Chrome/52.0.2743.82 Electron/1.3.1 Safari/537.36" , true } ,
{ "Edge 14" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393" , true } ,
{ "Internet Explorer 9" , "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0" , false } ,
2019-08-15 14:30:40 -04:00
{ "Internet Explorer 11" , "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" , false } ,
{ "Internet Explorer 11 2" , "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Zoom 3.6.0; rv:11.0) like Gecko" , false } ,
2018-04-09 06:14:36 -04:00
{ "Internet Explorer 11 (Compatibility Mode) 1" , "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET CLR 1.1.4322; InfoPath.3; Zoom 3.6.0)" , false } ,
{ "Internet Explorer 11 (Compatibility Mode) 2" , "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Zoom 3.6.0)" , false } ,
2019-11-04 17:01:29 +01:00
{ "Safari 12" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15" , true } ,
{ "Safari 11" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38" , false } ,
{ "Safari 10" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8" , false } ,
{ "Safari 9" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4" , false } ,
2018-03-06 13:22:07 -08:00
{ "Safari 8" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12" , false } ,
2019-11-04 17:01:29 +01:00
{ "Safari Mobile 12" , "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like macOS) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/12.0 Mobile/14A5335d Safari/602.1.50" , true } ,
{ "Safari Mobile 9" , "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B137 Safari/601.1" , false } ,
2018-03-06 13:22:07 -08:00
}
for _ , browser := range uaTestParameters {
2018-03-12 10:21:20 -04:00
t . Run ( browser . Name , func ( t * testing . T ) {
2019-10-21 17:00:21 +02:00
result := CheckClientCompatability ( browser . UserAgent )
require . Equalf ( t , result , browser . Result , "user agent test failed for %s" , browser . Name )
2018-03-12 10:21:20 -04:00
} )
2018-03-06 13:22:07 -08:00
}
2017-10-16 08:09:43 -07:00
}