mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-03 20:40:00 -05:00
Some checks are pending
API / build (push) Waiting to run
Server CI / Compute Go Version (push) Waiting to run
Server CI / Check mocks (push) Blocked by required conditions
Server CI / Check go mod tidy (push) Blocked by required conditions
Server CI / check-style (push) Blocked by required conditions
Server CI / Check serialization methods for hot structs (push) Blocked by required conditions
Server CI / Vet API (push) Blocked by required conditions
Server CI / Check migration files (push) Blocked by required conditions
Server CI / Generate email templates (push) Blocked by required conditions
Server CI / Check store layers (push) Blocked by required conditions
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (push) Blocked by required conditions
Server CI / Postgres (FIPS) (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
615 lines
17 KiB
Go
615 lines
17 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package utils
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
)
|
|
|
|
func TestStringArrayIntersection(t *testing.T) {
|
|
a := []string{
|
|
"abc",
|
|
"def",
|
|
"ghi",
|
|
}
|
|
b := []string{
|
|
"jkl",
|
|
}
|
|
c := []string{
|
|
"def",
|
|
}
|
|
|
|
assert.Empty(t, StringArrayIntersection(a, b))
|
|
assert.Len(t, StringArrayIntersection(a, c), 1)
|
|
}
|
|
|
|
func TestRemoveDuplicatesFromStringArray(t *testing.T) {
|
|
a := []string{
|
|
"a",
|
|
"b",
|
|
"a",
|
|
"a",
|
|
"b",
|
|
"c",
|
|
"a",
|
|
}
|
|
|
|
assert.Len(t, RemoveDuplicatesFromStringArray(a), 3)
|
|
}
|
|
|
|
func TestStringSliceDiff(t *testing.T) {
|
|
a := []string{"one", "two", "three", "four", "five", "six"}
|
|
b := []string{"two", "seven", "four", "six"}
|
|
expected := []string{"one", "three", "five"}
|
|
|
|
assert.Equal(t, expected, StringSliceDiff(a, b))
|
|
}
|
|
|
|
func TestGetIPAddress(t *testing.T) {
|
|
t.Run("Single IP in the X-Forwarded-For", func(t *testing.T) {
|
|
httpRequest1 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.0.0.1"},
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.0.0.1", GetIPAddress(&httpRequest1, []string{"X-Forwarded-For"}))
|
|
})
|
|
|
|
t.Run("Multiple IPs in the X-Forwarded-For", func(t *testing.T) {
|
|
httpRequest2 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.0.0.1, 10.0.0.2, 10.0.0.3"},
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.0.0.1", GetIPAddress(&httpRequest2, []string{"X-Forwarded-For"}))
|
|
})
|
|
|
|
t.Run("Empty X-Forwarded-For", func(t *testing.T) {
|
|
httpRequest3 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{""},
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest3, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
|
})
|
|
|
|
t.Run("Without an X-Forwarded-For", func(t *testing.T) {
|
|
httpRequest4 := http.Request{
|
|
Header: http.Header{
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest4, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
|
})
|
|
|
|
t.Run("Without any headers", func(t *testing.T) {
|
|
httpRequest5 := http.Request{
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.2.0.1", GetIPAddress(&httpRequest5, []string{"X-Forwarded-For", "X-Real-Ip"}))
|
|
})
|
|
|
|
t.Run("Two headers, but both untrusted", func(t *testing.T) {
|
|
httpRequest6 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.3.0.1"},
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.2.0.1", GetIPAddress(&httpRequest6, nil))
|
|
})
|
|
|
|
t.Run("Two headers, but only X-Real-Ip trusted", func(t *testing.T) {
|
|
httpRequest7 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.3.0.1"},
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest7, []string{"X-Real-Ip"}))
|
|
})
|
|
|
|
t.Run("X-Forwarded-For, comma separated, untrusted", func(t *testing.T) {
|
|
httpRequest8 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.3.0.1, 10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.2.0.1", GetIPAddress(&httpRequest8, nil))
|
|
})
|
|
|
|
t.Run("X-Forwarded-For, comma separated, untrusted", func(t *testing.T) {
|
|
httpRequest9 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.3.0.1, 10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.3.0.1", GetIPAddress(&httpRequest9, []string{"X-Forwarded-For"}))
|
|
})
|
|
|
|
t.Run("Two headers, both allowed, first one in trusted used", func(t *testing.T) {
|
|
httpRequest10 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.3.0.1"},
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.1.0.1", GetIPAddress(&httpRequest10, []string{"X-Real-Ip", "X-Forwarded-For"}))
|
|
})
|
|
|
|
t.Run("Multiple IPs in the X-Forwarded-For with no spaces", func(t *testing.T) {
|
|
httpRequest11 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"10.0.0.1,10.0.0.2,10.0.0.3"},
|
|
"X-Real-Ip": []string{"10.1.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.0.0.1", GetIPAddress(&httpRequest11, []string{"X-Forwarded-For"}))
|
|
})
|
|
|
|
t.Run("Make sure that the parsed value from headers only accept IP Addresses", func(t *testing.T) {
|
|
httpRequest12 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"127.0.0.1"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "127.0.0.1", GetIPAddress(&httpRequest12, []string{"X-Forwarded-For"}))
|
|
|
|
httpRequest13 := http.Request{
|
|
Header: http.Header{
|
|
"X-Forwarded-For": []string{"localhost"},
|
|
},
|
|
RemoteAddr: "10.2.0.1:12345",
|
|
}
|
|
|
|
assert.Equal(t, "10.2.0.1", GetIPAddress(&httpRequest13, []string{"X-Forwarded-For"}))
|
|
})
|
|
}
|
|
|
|
func TestRemoveStringFromSlice(t *testing.T) {
|
|
a := []string{"one", "two", "three", "four", "five", "six"}
|
|
expected := []string{"one", "two", "three", "five", "six"}
|
|
|
|
assert.Equal(t, RemoveStringFromSlice("four", a), expected)
|
|
}
|
|
|
|
func TestAppendQueryParamsToURL(t *testing.T) {
|
|
url := "mattermost://callback"
|
|
redirectURL := AppendQueryParamsToURL(url, map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
})
|
|
expected := url + "?key1=value1&key2=value2"
|
|
assert.Equal(t, redirectURL, expected)
|
|
}
|
|
|
|
func TestRoundOffToZeroes(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
n float64
|
|
expected int64
|
|
}{
|
|
{
|
|
desc: "returns 0 when n is 0",
|
|
n: 0,
|
|
expected: 0,
|
|
},
|
|
{
|
|
desc: "returns 0 when n is 9",
|
|
n: 9,
|
|
expected: 0,
|
|
},
|
|
{
|
|
desc: "returns 10 when n is 10",
|
|
n: 10,
|
|
expected: 10,
|
|
},
|
|
{
|
|
desc: "returns 90 when n is 99",
|
|
n: 99,
|
|
expected: 90,
|
|
},
|
|
{
|
|
desc: "returns 100 when n is 100",
|
|
n: 100,
|
|
expected: 100,
|
|
},
|
|
{
|
|
desc: "returns 100 when n is 101",
|
|
n: 101,
|
|
expected: 100,
|
|
},
|
|
{
|
|
desc: "returns 4000 when n is 4321",
|
|
n: 4321,
|
|
expected: 4000,
|
|
},
|
|
{
|
|
desc: "returns 0 when n is -9",
|
|
n: -9,
|
|
expected: 0,
|
|
},
|
|
{
|
|
desc: "returns -4000 when n is -4321",
|
|
n: -4321,
|
|
expected: -4000,
|
|
},
|
|
{
|
|
desc: "returns 4000 when n is 4321.235",
|
|
n: 4321.235,
|
|
expected: 4000,
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
res := RoundOffToZeroes(tc.n)
|
|
assert.Equal(t, tc.expected, res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsMobileRequest(t *testing.T) {
|
|
testCases := []struct {
|
|
userAgent string
|
|
expected bool
|
|
}{
|
|
// Test cases with mobile devices
|
|
{"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)", true},
|
|
{"Mozilla/5.0 (Android 12; Mobile)", true},
|
|
{"Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.99 Mobile Safari/537.36", true},
|
|
|
|
// Test cases with NO mobile devices
|
|
{"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", false},
|
|
{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", false},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("User-Agent", tc.userAgent)
|
|
|
|
result := IsMobileRequest(req)
|
|
if result != tc.expected {
|
|
t.Errorf("User-Agent: %s, expected: %v, got: %v", tc.userAgent, tc.expected, result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRoundOffToZeroesResolution(t *testing.T) {
|
|
messageGranularity := 3
|
|
storageGranularity := 8
|
|
testCases := []struct {
|
|
desc string
|
|
n float64
|
|
minResolution int
|
|
expected int64
|
|
}{
|
|
{
|
|
desc: "returns 0 when n is 0",
|
|
n: 0,
|
|
expected: 0,
|
|
},
|
|
{
|
|
desc: "resolution of 0 does not round",
|
|
n: 12345,
|
|
expected: 12345,
|
|
minResolution: 0,
|
|
},
|
|
{
|
|
desc: "resolution of 1 truncates to 10s: 9 -> 0",
|
|
n: 9,
|
|
expected: 0,
|
|
minResolution: 1,
|
|
},
|
|
{
|
|
desc: "resolution of 1 truncates to 10s: 10 -> 10",
|
|
n: 10,
|
|
expected: 10,
|
|
minResolution: 1,
|
|
},
|
|
{
|
|
desc: "resolution of 1 truncates to 10s: 11 -> 10",
|
|
n: 11,
|
|
expected: 10,
|
|
minResolution: 1,
|
|
},
|
|
{
|
|
desc: "resolution of 1 truncates to 10s: 19 -> 10",
|
|
n: 19,
|
|
expected: 10,
|
|
minResolution: 1,
|
|
},
|
|
{
|
|
desc: "supports message usage granularity (1000s): 9 -> 0",
|
|
n: 9,
|
|
expected: 0,
|
|
minResolution: messageGranularity,
|
|
},
|
|
{
|
|
desc: "supports message usage granularity (1000s): 123 -> 100",
|
|
n: 9,
|
|
expected: 0,
|
|
minResolution: messageGranularity,
|
|
},
|
|
{
|
|
desc: "supports message usage granularity (1000s): 1234 -> 1000",
|
|
n: 1234,
|
|
expected: 1000,
|
|
minResolution: messageGranularity,
|
|
},
|
|
{
|
|
desc: "supports message usage granularity (1000s): 1500 -> 1000",
|
|
n: 1500,
|
|
expected: 1000,
|
|
minResolution: messageGranularity,
|
|
},
|
|
{
|
|
desc: "supports file storage usage granularity (~100s of MiB): 13GiB -> 12.94GiB",
|
|
n: 13 * 1024 * 1024 * 1024,
|
|
expected: 13_900_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
{
|
|
desc: "supports file storage usage granularity (~100s of MiB): 10GiB -> 9.965GiB",
|
|
n: 10 * 1024 * 1024 * 1024,
|
|
expected: 10_700_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
{
|
|
// first number at which usage reports as in excess of 10GiB.
|
|
// Evaluates to 10299.5992MiB
|
|
// Should be close enough for notifying of being over limit.
|
|
desc: "supports file storage usage granularity (~100s of MiB): 10.0583GiB -> 10.0583GiB",
|
|
n: 10.0583 * 1024 * 1024 * 1024,
|
|
expected: 10_800_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
{
|
|
desc: "supports file storage usage granularity (~100s of MiB): 953.67MiB -> 953.67MiB",
|
|
n: 1_000_000_000,
|
|
expected: 1_000_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
{
|
|
desc: "supports file storage usage granularity (~100s of MiB): 1GiB -> 953.67MiB",
|
|
n: 1 * 1024 * 1024 * 1024,
|
|
expected: 1_000_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
{
|
|
desc: "supports file storage usage granularity (~100s of MiB): 1049.04MiB -> 1049.04MiB",
|
|
n: 1_100_000_000,
|
|
expected: 1_100_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
{
|
|
desc: "supports file storage usage granularity (smaller amounts): 104.904MiB -> 104.904MiB",
|
|
n: 100_000_000,
|
|
expected: 100_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
{
|
|
desc: "supports file storage usage granularity (smaller amounts): 10.4904MiB -> 10.4904MiB",
|
|
n: 10_000_000,
|
|
expected: 10_000_000,
|
|
minResolution: storageGranularity,
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
res := RoundOffToZeroesResolution(tc.n, tc.minResolution)
|
|
assert.Equal(t, tc.expected, res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsValidWebAuthRedirectURL(t *testing.T) {
|
|
t.Run("Valid redirect URL with matching scheme and host", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com"),
|
|
},
|
|
}
|
|
redirectURL := "https://example.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Valid redirect URL with matching scheme and host with port", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com:8080"),
|
|
},
|
|
}
|
|
redirectURL := "https://example.com:8080/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Invalid redirect URL with different scheme", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com"),
|
|
},
|
|
}
|
|
redirectURL := "http://example.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "scheme")
|
|
})
|
|
|
|
t.Run("Invalid redirect URL with different host", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com"),
|
|
},
|
|
}
|
|
redirectURL := "https://malicious.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "host")
|
|
})
|
|
|
|
t.Run("Invalid redirect URL with different port", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com:8080"),
|
|
},
|
|
}
|
|
redirectURL := "https://example.com:9090/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "host")
|
|
})
|
|
|
|
t.Run("Invalid redirect URL - malformed URL", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com"),
|
|
},
|
|
}
|
|
redirectURL := "://not-a-valid-url"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to parse redirect URL")
|
|
})
|
|
|
|
t.Run("Invalid config - nil SiteURL", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: nil,
|
|
},
|
|
}
|
|
redirectURL := "https://example.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "SiteURL is not configured")
|
|
})
|
|
|
|
t.Run("Invalid config - malformed SiteURL", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("://not-a-valid-url"),
|
|
},
|
|
}
|
|
redirectURL := "https://example.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to parse SiteURL")
|
|
})
|
|
|
|
t.Run("Valid redirect URL with subdomain", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://app.example.com"),
|
|
},
|
|
}
|
|
redirectURL := "https://app.example.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Invalid redirect URL with different subdomain", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://app.example.com"),
|
|
},
|
|
}
|
|
redirectURL := "https://api.example.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "host")
|
|
})
|
|
|
|
t.Run("Valid redirect URL with path", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com/mattermost"),
|
|
},
|
|
}
|
|
redirectURL := "https://example.com/mattermost/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Valid redirect URL with query parameters", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com"),
|
|
},
|
|
}
|
|
redirectURL := "https://example.com/oauth/callback?state=abc123&code=def456"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Valid redirect URL with fragment", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://example.com"),
|
|
},
|
|
}
|
|
redirectURL := "https://example.com/oauth/callback#token=abc123"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Invalid redirect URL with @ symbol in host", func(t *testing.T) {
|
|
config := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewPointer("https://qa-release.test.mattermost.cloud"),
|
|
},
|
|
}
|
|
redirectURL := "https://qa-release.test.mattermost.cloud@example.com/oauth/callback"
|
|
|
|
err := ValidateWebAuthRedirectUrl(config, redirectURL)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "host")
|
|
})
|
|
}
|