feat: show application ownership and scoped console management
This commit is contained in:
78
internal/transport/httpserver/console_scope_test.go
Normal file
78
internal/transport/httpserver/console_scope_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.yuntuo.hk/k2safe/orbit/internal/consoleauth"
|
||||
core "git.yuntuo.hk/k2safe/orbit/model"
|
||||
"git.yuntuo.hk/k2safe/orbit/platform/auth"
|
||||
)
|
||||
|
||||
type fakeConsoleScopes struct{ calls int }
|
||||
|
||||
func (s *fakeConsoleScopes) ResolveScope(_ context.Context, t auth.Token, target core.ID) (auth.Token, error) {
|
||||
s.calls++
|
||||
t.Console = &auth.ConsoleIdentity{SessionNamespaceID: t.NamespaceID, Username: "admin", UserID: t.CredentialID}
|
||||
t.NamespaceID = target
|
||||
return t, nil
|
||||
}
|
||||
func (*fakeConsoleScopes) GetScope(context.Context, auth.Token, core.ID) (consoleauth.Scope, error) {
|
||||
return consoleauth.Scope{}, nil
|
||||
}
|
||||
func TestConsoleScopeOnlyBridgesCookieManagementRequests(t *testing.T) {
|
||||
ns, _ := core.NewID()
|
||||
cred, _ := core.NewID()
|
||||
target, _ := core.NewID()
|
||||
raw, _, _ := auth.Issue(ns, cred)
|
||||
for _, tc := range []struct {
|
||||
name, method, path, origin, bearer string
|
||||
cookie, marker bool
|
||||
status int
|
||||
}{
|
||||
{"read", "GET", "/v1/tasks", "", "", true, true, 200},
|
||||
{"control", "POST", "/v1/tasks/task:cancel", "http://example.com", "", true, true, 200},
|
||||
{"SDK", "GET", "/v1/tasks", "", "Bearer " + raw, false, true, 403},
|
||||
{"ambiguous", "GET", "/v1/tasks", "", "Bearer " + raw, true, true, 403},
|
||||
{"anonymous", "GET", "/v1/tasks", "", "", false, true, 401},
|
||||
{"missing-marker", "GET", "/v1/tasks", "", "", true, false, 403},
|
||||
{"cross-origin", "POST", "/v1/tasks/task:cancel", "https://other.invalid", "", true, true, 403},
|
||||
{"worker", "GET", "/v1/workers/connect", "", "", true, true, 403},
|
||||
{"create", "POST", "/v1/tasks", "", "", true, true, 403},
|
||||
{"onboarding", "POST", "/console/applications/prepare", "", "", true, true, 403},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
scope := &fakeConsoleScopes{}
|
||||
handler := ConsoleSessionMiddleware(wrap(func(r *http.Request, tok auth.Token) (any, int, error) {
|
||||
if tok.NamespaceID != target || tok.Console == nil || tok.Console.SessionNamespaceID != ns {
|
||||
t.Fatal("scope lost")
|
||||
}
|
||||
return map[string]bool{"ok": true}, 200, nil
|
||||
}), &fakeConsoleAuth{valid: true}, scope)
|
||||
req := httptest.NewRequest(tc.method, tc.path, nil)
|
||||
req.Header.Set("X-Orbit-Namespace", string(target))
|
||||
if tc.marker {
|
||||
req.Header.Set("X-Orbit-Console", "1")
|
||||
}
|
||||
if tc.origin != "" {
|
||||
req.Header.Set("Origin", tc.origin)
|
||||
}
|
||||
if tc.bearer != "" {
|
||||
req.Header.Set("Authorization", tc.bearer)
|
||||
}
|
||||
if tc.cookie {
|
||||
req.AddCookie(&http.Cookie{Name: consoleSessionCookie, Value: raw})
|
||||
}
|
||||
res := httptest.NewRecorder()
|
||||
handler.ServeHTTP(res, req)
|
||||
if res.Code != tc.status {
|
||||
t.Fatalf("HTTP %d: %s", res.Code, res.Body.String())
|
||||
}
|
||||
if tc.status != 200 && scope.calls != 0 {
|
||||
t.Fatal("rejected request reached scope resolver")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user