mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2026-10-10 04:53:09 +08:00
ea10624fb6
Co-authored-by: PIKACHUIM <PIKACHUIM@users.noreply.github.com>
103 lines
3.7 KiB
Go
103 lines
3.7 KiB
Go
package handles
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/OpenListTeam/OpenList/v4/internal/conf"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func ssoTestContext(apiUrl string) (*gin.Context, *httptest.ResponseRecorder) {
|
|
gin.SetMode(gin.TestMode)
|
|
rec := httptest.NewRecorder()
|
|
c, engine := gin.CreateTestContext(rec)
|
|
// Matches server.Init, which is what lets GetApiUrl reach the value the
|
|
// middleware stored on the request context.
|
|
engine.ContextWithFallback = true
|
|
req := httptest.NewRequest(http.MethodGet, "/api/auth/sso?method=sso_get_token", nil)
|
|
if apiUrl != "" {
|
|
req = req.WithContext(context.WithValue(req.Context(), conf.ApiUrlKey, apiUrl))
|
|
}
|
|
c.Request = req
|
|
// Keep setting lookups off the (uninitialised) database: ssoTargetOrigin
|
|
// reads sso_postmessage_origin through the setting cache.
|
|
op.Cache.SetSetting(conf.SSOPostMessageOrigin, &model.SettingItem{
|
|
Key: conf.SSOPostMessageOrigin,
|
|
Value: "",
|
|
})
|
|
return c, rec
|
|
}
|
|
|
|
// A page that opens the SSO endpoint in a popup must not be able to read the
|
|
// token: the postMessage target origin has to name the site, never "*".
|
|
func TestSSOPostMessagePinsTargetOrigin(t *testing.T) {
|
|
c, rec := ssoTestContext("https://openlist.example.com/base")
|
|
ssoPostMessage(c, map[string]string{"token": "secret-token"})
|
|
|
|
body := rec.Body.String()
|
|
if strings.Contains(body, `"*"`) || strings.Contains(body, `, '*'`) {
|
|
t.Fatalf("wildcard target origin present in response:\n%s", body)
|
|
}
|
|
if !strings.Contains(body, `"https://openlist.example.com"`) {
|
|
t.Errorf("expected the site origin as target, got:\n%s", body)
|
|
}
|
|
if !strings.Contains(body, "secret-token") {
|
|
t.Errorf("payload should still reach a legitimate opener, got:\n%s", body)
|
|
}
|
|
}
|
|
|
|
// A frontend served from a different origin than the API needs the operator to
|
|
// be able to point the target at the frontend origin. The configured origin
|
|
// must win over the API origin.
|
|
func TestSSOPostMessageUsesConfiguredOrigin(t *testing.T) {
|
|
c, rec := ssoTestContext("https://api.example.com/base")
|
|
op.Cache.SetSetting(conf.SSOPostMessageOrigin, &model.SettingItem{
|
|
Key: conf.SSOPostMessageOrigin,
|
|
Value: "https://frontend.example.com",
|
|
})
|
|
defer op.Cache.ClearAll()
|
|
|
|
ssoPostMessage(c, map[string]string{"token": "secret-token"})
|
|
|
|
body := rec.Body.String()
|
|
if !strings.Contains(body, `"https://frontend.example.com"`) {
|
|
t.Errorf("expected the configured origin as target, got:\n%s", body)
|
|
}
|
|
}
|
|
|
|
// If the site URL cannot be resolved the fallback must tighten delivery to
|
|
// same-origin openers, not widen it back to every origin.
|
|
func TestSSOPostMessageFallsBackToSameOrigin(t *testing.T) {
|
|
c, rec := ssoTestContext("")
|
|
ssoPostMessage(c, map[string]string{"token": "secret-token"})
|
|
|
|
body := rec.Body.String()
|
|
if strings.Contains(body, `"*"`) {
|
|
t.Fatalf("fallback must not be a wildcard origin:\n%s", body)
|
|
}
|
|
if !strings.Contains(body, `"/"`) {
|
|
t.Errorf(`expected "/" fallback origin, got:\n%s`, body)
|
|
}
|
|
}
|
|
|
|
// userID comes from the identity provider, so it must be encoded rather than
|
|
// interpolated into the JS string literal it used to land in.
|
|
func TestSSOPostMessageEscapesProviderControlledValue(t *testing.T) {
|
|
c, rec := ssoTestContext("https://openlist.example.com")
|
|
ssoPostMessage(c, map[string]string{"sso_id": `"});alert(document.domain);//`})
|
|
|
|
body := rec.Body.String()
|
|
if strings.Contains(body, `alert(document.domain)`) && !strings.Contains(body, `\"`) {
|
|
t.Fatalf("provider value was not escaped:\n%s", body)
|
|
}
|
|
if !strings.Contains(body, `\"});alert`) {
|
|
t.Errorf("expected the injected quote to be escaped, got:\n%s", body)
|
|
}
|
|
}
|