Add approval:read to informed-decision-sitting-requester (source only)
informed-decision's present_existing intake (INFD-WP-0010, 0e66a33) must GET an existing approval before presenting it. This grants read only: no consume, no approve, and the audience, subject, tenant and secret stay the same. The tests pin the exact scope pair and keep the published JSON equal to the source row. The live registry is not changed. docs/sitting-requester-read-scope.md has the record diff and the founder-run keycape-client.py remove/add/restart steps, and lists the railiance-platform and informed-decision copies that still pin create-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5502@bnt-lap001 Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
This commit is contained in:
parent
655d8d31b3
commit
82b471fc36
5 changed files with 202 additions and 5 deletions
|
|
@ -4,9 +4,12 @@ import (
|
|||
"keycape/internal/config"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestServiceRegistrationAudienceAndScopeIsolation(t *testing.T) {
|
||||
|
|
@ -184,14 +187,31 @@ func TestSittingRequesterRegistrationShapeIsExact(t *testing.T) {
|
|||
if client.Tenant != "tenant:platform" || client.Audience != "approval-engine" {
|
||||
t.Errorf("tenant/audience = %q %q", client.Tenant, client.Audience)
|
||||
}
|
||||
if len(client.AllowedScopes) != 1 || client.AllowedScopes[0] != "approval:create" {
|
||||
t.Fatalf("scopes = %v; want only approval:create", client.AllowedScopes)
|
||||
// create to file its own sittings; read (2026-11-01, INFD-WP-0010) to GET an
|
||||
// existing approval before presenting it. Never consume or approve.
|
||||
if !reflect.DeepEqual(client.AllowedScopes, []string{"approval:create", "approval:read"}) {
|
||||
t.Fatalf("scopes = %v; want exactly approval:create, approval:read", client.AllowedScopes)
|
||||
}
|
||||
for _, scope := range client.AllowedScopes {
|
||||
if scope == "approval:consume" || scope == "approval:approve" || scope == "approval:read" {
|
||||
if scope == "approval:consume" || scope == "approval:approve" || scope == "openid" {
|
||||
t.Fatalf("excess sitting-requester scope %q", scope)
|
||||
}
|
||||
}
|
||||
if len(client.Roles) != 1 || client.Roles[0] != "informed-decision-sitting-requester" || client.TokenLifetime != "15m" {
|
||||
t.Errorf("roles/lifetime = %v %q", client.Roles, client.TokenLifetime)
|
||||
}
|
||||
// The machine-readable copy that the live apply reads must equal the source row.
|
||||
raw, err := os.ReadFile("../../../docs/sitting-requester-registration.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var published config.ClientConfig // JSON is YAML; decode by the struct's yaml tags
|
||||
if err := yaml.Unmarshal(raw, &published); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(published, *client) {
|
||||
t.Fatalf("docs/sitting-requester-registration.json drifted from the source row:\n%+v\n%+v", published, *client)
|
||||
}
|
||||
if len(client.RedirectURIs) != 0 {
|
||||
t.Errorf("redirect URIs = %v; sitting-requester is not a browser client", client.RedirectURIs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func TestIdentityFactsCommittedServiceClients(t *testing.T) {
|
|||
"secrets-engine-approval": {"approval:read", "approval:consume"},
|
||||
"secrets-engine-requester": {"approval:create"},
|
||||
"approval-engine-operator": {"approval:create", "approval:read", "approval:approve", "approval:revoke", "approval:supersede", "approval:observe", "approval:emit"},
|
||||
"informed-decision-sitting-requester": {"approval:create"},
|
||||
"informed-decision-sitting-requester": {"approval:create", "approval:read"},
|
||||
"secrets-engine-openbao": {"openbao:login"},
|
||||
}
|
||||
seen := 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue