The identity fingerprint no longer includes token ids: guards decide on scopes, subjects and the ceiling, and an OAuth access token's id changes on every refresh, so a rotation alone no longer re-filters a cached server. The stateless path's refresh only adds tools and prompts for a fingerprint new to the entry (remembered until a fan-out recompute) and never removes: one server serves every live token of a binding, and removing on each alternation between a wider and a narrower token pulled a tool from under the wider token's call. The receiving middleware still filters every list and call per request; fan-out recomputes still narrow. The stateful path keeps its exact widen, a session serves one client. TestFingerprintIgnoresTokenID pins the fingerprint; TestStatelessSharedBinding blocks the wide token's call after its server was chosen, sends a request of the narrow token and asserts the call still succeeds (it failed with "unknown tool" before).
35 lines
1.3 KiB
Go
35 lines
1.3 KiB
Go
package pier
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// TestFingerprintIgnoresTokenID pins RA-WR-02: an OAuth refresh changes the
|
|
// access-token id only, which must not re-filter a cached server, while a
|
|
// change of scopes, subjects or ceiling still does.
|
|
func TestFingerprintIgnoresTokenID(t *testing.T) {
|
|
id := func(token uint, scopes ...string) Identity {
|
|
return Identity{
|
|
Scopes: scopes,
|
|
Binding: "oauth:c1:7",
|
|
TokenKind: TokenKindOAuth,
|
|
ceiling: true,
|
|
Frontend: &pact.MCPSubject{Guard: "user", UserID: 7, ClientID: "c1", TokenID: token, Scopes: scopes},
|
|
Backend: &pact.MCPSubject{Guard: BackendGuard, UserID: 42, ClientID: "c1", TokenID: token, Scopes: scopes},
|
|
}
|
|
}
|
|
if a, b := id(1, "read", "backend").fingerprint(), id(2, "read", "backend").fingerprint(); a != b {
|
|
t.Fatalf("a token rotation changed the fingerprint:\n%s\n%s", a, b)
|
|
}
|
|
if a, b := id(1, "read", "backend").fingerprint(), id(1, "read", "write", "backend").fingerprint(); a == b {
|
|
t.Fatal("a scope change kept the fingerprint")
|
|
}
|
|
noBackend := id(1, "read", "backend")
|
|
noBackend.Backend = nil
|
|
if noBackend.fingerprint() == id(1, "read", "backend").fingerprint() {
|
|
t.Fatal("dropping the backend subject kept the fingerprint")
|
|
}
|
|
}
|