1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-02-06 16:51:33 +00:00
pocketbase/core/event_request_test.go
2024-12-11 19:00:28 +02:00

335 lines
8.6 KiB
Go

package core_test
import (
"encoding/json"
"net/http"
"strings"
"testing"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/tests"
)
func TestEventRequestRealIP(t *testing.T) {
t.Parallel()
headers := map[string][]string{
"CF-Connecting-IP": {"1.2.3.4", "1.1.1.1"},
"Fly-Client-IP": {"1.2.3.4", "1.1.1.2"},
"X-Real-IP": {"1.2.3.4", "1.1.1.3,1.1.1.4"},
"X-Forwarded-For": {"1.2.3.4", "invalid,1.1.1.5,1.1.1.6,invalid"},
}
scenarios := []struct {
name string
headers map[string][]string
trustedHeaders []string
useLeftmostIP bool
expected string
}{
{
"no trusted headers",
headers,
nil,
false,
"127.0.0.1",
},
{
"non-matching trusted header",
headers,
[]string{"header1", "header2"},
false,
"127.0.0.1",
},
{
"trusted X-Real-IP (rightmost)",
headers,
[]string{"header1", "x-real-ip", "x-forwarded-for"},
false,
"1.1.1.4",
},
{
"trusted X-Real-IP (leftmost)",
headers,
[]string{"header1", "x-real-ip", "x-forwarded-for"},
true,
"1.1.1.3",
},
{
"trusted X-Forwarded-For (rightmost)",
headers,
[]string{"header1", "x-forwarded-for"},
false,
"1.1.1.6",
},
{
"trusted X-Forwarded-For (leftmost)",
headers,
[]string{"header1", "x-forwarded-for"},
true,
"1.1.1.5",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
app, err := tests.NewTestApp()
if err != nil {
t.Fatal(err)
}
defer app.Cleanup()
app.Settings().TrustedProxy.Headers = s.trustedHeaders
app.Settings().TrustedProxy.UseLeftmostIP = s.useLeftmostIP
event := core.RequestEvent{}
event.App = app
event.Request, err = http.NewRequest(http.MethodGet, "/", nil)
if err != nil {
t.Fatal(err)
}
event.Request.RemoteAddr = "127.0.0.1:80" // fallback
for k, values := range s.headers {
for _, v := range values {
event.Request.Header.Add(k, v)
}
}
result := event.RealIP()
if result != s.expected {
t.Fatalf("Expected ip %q, got %q", s.expected, result)
}
})
}
}
func TestEventRequestHasSuperUserAuth(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
if err != nil {
t.Fatal(err)
}
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, "test@example.com")
if err != nil {
t.Fatal(err)
}
scenarios := []struct {
name string
record *core.Record
expected bool
}{
{"nil record", nil, false},
{"regular user record", user, false},
{"superuser record", superuser, true},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
e := core.RequestEvent{}
e.Auth = s.record
result := e.HasSuperuserAuth()
if result != s.expected {
t.Fatalf("Expected %v, got %v", s.expected, result)
}
})
}
}
func TestRequestEventRequestInfo(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
userCol, err := app.FindCollectionByNameOrId("users")
if err != nil {
t.Fatal(err)
}
user1 := core.NewRecord(userCol)
user1.Id = "user1"
user1.SetEmail("test1@example.com")
user2 := core.NewRecord(userCol)
user2.Id = "user2"
user2.SetEmail("test2@example.com")
testBody := `{"a":123,"b":"test"}`
event := core.RequestEvent{}
event.Request, err = http.NewRequest("POST", "/test?q1=123&q2=456", strings.NewReader(testBody))
if err != nil {
t.Fatal(err)
}
event.Request.Header.Add("content-type", "application/json")
event.Request.Header.Add("x-test", "test")
event.Set(core.RequestEventKeyInfoContext, "test")
event.Auth = user1
t.Run("init", func(t *testing.T) {
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
raw, err := json.Marshal(info)
if err != nil {
t.Fatalf("Failed to serialize request info: %v", err)
}
rawStr := string(raw)
expected := `{"query":{"q1":"123","q2":"456"},"headers":{"content_type":"application/json","x_test":"test"},"body":{"a":123,"b":"test"},"auth":{"avatar":"","collectionId":"_pb_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user1","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"test"}`
if expected != rawStr {
t.Fatalf("Expected\n%v\ngot\n%v", expected, rawStr)
}
})
t.Run("change user and context", func(t *testing.T) {
event.Set(core.RequestEventKeyInfoContext, "test2")
event.Auth = user2
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
raw, err := json.Marshal(info)
if err != nil {
t.Fatalf("Failed to serialize request info: %v", err)
}
rawStr := string(raw)
expected := `{"query":{"q1":"123","q2":"456"},"headers":{"content_type":"application/json","x_test":"test"},"body":{"a":123,"b":"test"},"auth":{"avatar":"","collectionId":"_pb_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user2","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"test2"}`
if expected != rawStr {
t.Fatalf("Expected\n%v\ngot\n%v", expected, rawStr)
}
})
}
func TestRequestInfoHasSuperuserAuth(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
if err != nil {
t.Fatal(err)
}
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, "test@example.com")
if err != nil {
t.Fatal(err)
}
event := core.RequestEvent{}
event.Request, err = http.NewRequest("POST", "/test?q1=123&q2=456", strings.NewReader(`{"a":123,"b":"test"}`))
if err != nil {
t.Fatal(err)
}
event.Request.Header.Add("content-type", "application/json")
scenarios := []struct {
name string
record *core.Record
expected bool
}{
{"nil record", nil, false},
{"regular user record", user, false},
{"superuser record", superuser, true},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
event.Auth = s.record
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
result := info.HasSuperuserAuth()
if result != s.expected {
t.Fatalf("Expected %v, got %v", s.expected, result)
}
})
}
}
func TestRequestInfoClone(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
userCol, err := app.FindCollectionByNameOrId("users")
if err != nil {
t.Fatal(err)
}
user := core.NewRecord(userCol)
user.Id = "user1"
user.SetEmail("test1@example.com")
event := core.RequestEvent{}
event.Request, err = http.NewRequest("POST", "/test?q1=123&q2=456", strings.NewReader(`{"a":123,"b":"test"}`))
if err != nil {
t.Fatal(err)
}
event.Request.Header.Add("content-type", "application/json")
event.Auth = user
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
clone := info.Clone()
// modify the clone fields to ensure that it is a shallow copy
clone.Headers["new_header"] = "test"
clone.Query["new_query"] = "test"
clone.Body["new_body"] = "test"
clone.Auth.Id = "user2" // should be a Fresh copy of the record
// check the original data
// ---
originalRaw, err := json.Marshal(info)
if err != nil {
t.Fatalf("Failed to serialize original request info: %v", err)
}
originalRawStr := string(originalRaw)
expectedRawStr := `{"query":{"q1":"123","q2":"456"},"headers":{"content_type":"application/json"},"body":{"a":123,"b":"test"},"auth":{"avatar":"","collectionId":"_pb_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user1","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"default"}`
if expectedRawStr != originalRawStr {
t.Fatalf("Expected original info\n%v\ngot\n%v", expectedRawStr, originalRawStr)
}
// check the clone data
// ---
cloneRaw, err := json.Marshal(clone)
if err != nil {
t.Fatalf("Failed to serialize clone request info: %v", err)
}
cloneRawStr := string(cloneRaw)
expectedCloneStr := `{"query":{"new_query":"test","q1":"123","q2":"456"},"headers":{"content_type":"application/json","new_header":"test"},"body":{"a":123,"b":"test","new_body":"test"},"auth":{"avatar":"","collectionId":"_pb_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user2","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"default"}`
if expectedCloneStr != cloneRawStr {
t.Fatalf("Expected clone info\n%v\ngot\n%v", expectedCloneStr, cloneRawStr)
}
}