Test the credential half of the rendering over an Input (issue 1364). Fixtures from mesh-control 6c2a8d62 outside_not_given_test.go (the assistant's two secrets) and the facts_test.go provider and consumer; the refusals checked word for word against plan.go and secrets.go. Written after the code, each rule broken in turn and a test seen to fail. The bus user list is an Input value, so its test is that it is carried (see the commit of the code). Unit level 1.04 s of 2 s.
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/overlay"
|
||||
)
|
||||
|
||||
// secretValue is a value the Input carries that must never appear in anything the composition says.
|
||||
const secretValue = "SECRET-VALUE-THAT-MUST-NOT-LEAK"
|
||||
|
||||
func rendered(t *testing.T, in Input, node string, mode Mode) Rendered {
|
||||
t.Helper()
|
||||
got, err := Render(in, node, mode)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
// anAssistant is mesh-controller's outside_not_given_test.go anAssistant (cmd/mesh-controller at 6c2a8d62): a module with a secret
|
||||
// the mesh makes and one only an outside party issues.
|
||||
func anAssistant() catalogue.Manifest {
|
||||
return catalogue.Manifest{Module: "assistant", Version: "1",
|
||||
OwnSecrets: catalogue.OwnSecrets{
|
||||
"password": {Path: "/var/lib/mesh/assistant/password", Taken: catalogue.TakenAtStart},
|
||||
"vendor-key": {Path: "/var/lib/mesh/assistant/vendor-key", Taken: catalogue.TakenAtStart, IssuedBy: catalogue.IssuedOutside},
|
||||
},
|
||||
Resources: []map[string]any{{"id": "state", "type": "directory", "path": "/var/lib/mesh/assistant", "mode": "0700"}}}
|
||||
}
|
||||
|
||||
func withAssistant(t *testing.T) Input {
|
||||
t.Helper()
|
||||
in := aMesh(t)
|
||||
in.Catalogue["assistant"] = anAssistant()
|
||||
laptop := nodeOf(t, &in, "laptop")
|
||||
laptop.Assigned = append(laptop.Assigned, "assistant")
|
||||
laptop.SealingKey = keyOf(t, "laptop")
|
||||
return in
|
||||
}
|
||||
|
||||
// Own secrets (plan.go renderingFor, novox/hq issue 275 and 401): one held and sealed to the machine's key is carried; one not held is
|
||||
// foreseen in the Foreseeing mode and left out in the Reading mode, and nothing is made.
|
||||
func TestAnOwnSecretHeldIsCarriedAndOneNotHeldIsForeseenOrLeftOut(t *testing.T) {
|
||||
mk := func() Input {
|
||||
in := withAssistant(t)
|
||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "password", Origin: "made",
|
||||
SealedTo: nodeOf(t, &in, "laptop").SealingKey, Sealed: secretValue}}
|
||||
return in
|
||||
}
|
||||
in := mk()
|
||||
got := rendered(t, in, "laptop", Foreseeing)
|
||||
if got.Rendering.Needed["assistant"]["password"] != secretValue || len(got.Rendering.Foreseen) != 0 {
|
||||
t.Errorf("a held secret: needed %v foreseen %v", got.Rendering.Needed, got.Rendering.Foreseen)
|
||||
}
|
||||
// Not held: foreseen with a stand-in (Foreseeing), left out (Reading).
|
||||
in.OwnSecrets = nil
|
||||
got = rendered(t, in, "laptop", Foreseeing)
|
||||
if !got.Rendering.Foreseen["assistant"]["password"] || got.Rendering.Needed["assistant"]["password"] != "" ||
|
||||
len(got.Foreseen) != 1 || got.Foreseen[0] != "assistant/password" {
|
||||
t.Errorf("a secret the send makes: foreseen %v / %v needed %v", got.Rendering.Foreseen, got.Foreseen, got.Rendering.Needed)
|
||||
}
|
||||
got = rendered(t, in, "laptop", Reading)
|
||||
if len(got.Rendering.Needed["assistant"]) != 0 || len(got.Foreseen) != 0 || got.Rendering.Foreseen != nil {
|
||||
t.Errorf("Reading composed a secret that was never issued: %v %v", got.Rendering.Needed, got.Foreseen)
|
||||
}
|
||||
// Held but sealed to a key the machine no longer has is not held.
|
||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "password", Origin: "made", SealedTo: "an-old-key", Sealed: secretValue}}
|
||||
if got := rendered(t, in, "laptop", Foreseeing); !got.Rendering.Foreseen["assistant"]["password"] {
|
||||
t.Errorf("a secret sealed to an old key is held: %v", got.Rendering.Needed)
|
||||
}
|
||||
// Held with no value in the Input: a stand-in, named.
|
||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "password", Origin: "made", SealedTo: nodeOf(t, &in, "laptop").SealingKey}}
|
||||
if got := rendered(t, in, "laptop", Reading); !got.Rendering.Foreseen["assistant"]["password"] {
|
||||
t.Errorf("a held secret with no value is not named: %v", got.Rendering.Foreseen)
|
||||
}
|
||||
unchanged(t, mk, func(in Input) { rendered(t, in, "laptop", Foreseeing) })
|
||||
}
|
||||
|
||||
// An own secret issued outside the mesh that nobody gave is never made (novox/hq issue 401, ADR 0228): nothing is composed or
|
||||
// foreseen for it; a value the mesh made for it is not one a person gave; a value given is placed once it is sealed to the key held now.
|
||||
func TestAnOutsideSecretIsPlacedOnlyOnceAPersonGaveIt(t *testing.T) {
|
||||
in := withAssistant(t)
|
||||
key := nodeOf(t, &in, "laptop").SealingKey
|
||||
for _, mode := range []Mode{Reading, Foreseeing} {
|
||||
got := rendered(t, in, "laptop", mode)
|
||||
if _, there := got.Rendering.Needed["assistant"]["vendor-key"]; there || got.Rendering.Foreseen["assistant"]["vendor-key"] {
|
||||
t.Fatalf("%v: a secret nobody gave is composed: %v %v", mode, got.Rendering.Needed, got.Rendering.Foreseen)
|
||||
}
|
||||
}
|
||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "vendor-key", Origin: "made", SealedTo: key, Sealed: secretValue}}
|
||||
if _, there := rendered(t, in, "laptop", Foreseeing).Rendering.Needed["assistant"]["vendor-key"]; there {
|
||||
t.Error("a value the mesh made for a secret issued outside was placed")
|
||||
}
|
||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "vendor-key", Origin: "accepted", SealedTo: key, Sealed: "the vendor's key, sealed"}}
|
||||
if got := rendered(t, in, "laptop", Foreseeing).Rendering.Needed["assistant"]["vendor-key"]; got != "the vendor's key, sealed" {
|
||||
t.Errorf("a value given is placed as %q", got)
|
||||
}
|
||||
in.OwnSecrets[0].SealedTo = "an-old-key"
|
||||
if _, there := rendered(t, in, "laptop", Foreseeing).Rendering.Needed["assistant"]["vendor-key"]; there {
|
||||
t.Error("a value given under an old key was placed")
|
||||
}
|
||||
}
|
||||
|
||||
// 1364/AC3 (the send's refusals, novox/hq issue 203 and 275): in the Foreseeing mode a bus credential nobody issued and a given secret
|
||||
// under an old key are refused in the controller's words; with the credential issued, the secret is foreseen. No secret value is in
|
||||
// an error.
|
||||
func TestTheSendsRefusalsAreRefusedInTheSendsWords(t *testing.T) {
|
||||
broker := catalogue.Manifest{Module: "gate", Version: "1",
|
||||
OwnSecrets: catalogue.OwnSecrets{"broker": {Path: "/var/lib/mesh/gate/broker", Taken: catalogue.TakenAtStart}}}
|
||||
in := withAssistant(t)
|
||||
in.Catalogue["gate"] = broker
|
||||
laptop := nodeOf(t, &in, "laptop")
|
||||
laptop.Assigned = append(laptop.Assigned, "gate")
|
||||
_, err := Render(in, "laptop", Foreseeing)
|
||||
want := "gate on laptop has no bus credential: nothing was issued for laptop.gate, and a push would seal a placeholder " +
|
||||
"its process cannot read (novox/hq issue 203). `module issue gate --node laptop`, then push again"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Fatalf("got %v, want %q", err, want)
|
||||
}
|
||||
// Reading refuses nothing for it: the machine is not running it.
|
||||
if _, err := Render(in, "laptop", Reading); err != nil {
|
||||
t.Fatalf("Reading refused a secret the machine is not running: %v", err)
|
||||
}
|
||||
in.BusMinted = []string{"laptop.gate"}
|
||||
if got := rendered(t, in, "laptop", Foreseeing); !got.Rendering.Foreseen["gate"]["broker"] {
|
||||
t.Errorf("an issued bus credential is not foreseen: %v", got.Rendering.Foreseen)
|
||||
}
|
||||
// A given secret under an old key.
|
||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "password", Origin: "accepted", SealedTo: "an-old-key", Sealed: secretValue},
|
||||
{Node: "laptop", Module: "gate", Name: "broker", Origin: "made", SealedTo: laptop.SealingKey, Sealed: secretValue}}
|
||||
_, err = Render(in, "laptop", Foreseeing)
|
||||
wantOld := "assistant on laptop holds \"password\", which was given to the mesh rather than made by it, and laptop has since generated a new " +
|
||||
"sealing key. The mesh cannot make another; issue it again"
|
||||
if err == nil || err.Error() != wantOld {
|
||||
t.Fatalf("got %v, want %q", err, wantOld)
|
||||
}
|
||||
if strings.Contains(err.Error(), secretValue) {
|
||||
t.Error("a secret value is in an error")
|
||||
}
|
||||
// A machine with no sealing key cannot be sealed to.
|
||||
laptop.SealingKey = ""
|
||||
in.OwnSecrets = nil
|
||||
if _, err := Render(in, "laptop", Foreseeing); err == nil || !strings.Contains(err.Error(), "laptop has no sealing key, so nothing can be sealed to it") {
|
||||
t.Fatalf("no sealing key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// aGrantMesh is the objects provider on anchor and its consumer files on laptop, with the pair credential made.
|
||||
func aGrantMesh(t *testing.T) Input {
|
||||
t.Helper()
|
||||
in := theObjectsMesh(t, objects())
|
||||
in.Issued = []Issued{{Provision: "s3-bucket", Consumer: "laptop", ConsumerModule: "files", Provider: "anchor", ForProvider: "sealed-to-anchor"}}
|
||||
return in
|
||||
}
|
||||
|
||||
// A credential made for a consumer is a grant on its provider's machine, carrying the consumer's values and the sealed copy; without
|
||||
// the sealed copy a stand-in is composed and named (novox/hq plan.go grantsFor).
|
||||
func TestAGrantIsCreatedForEachCredentialMadeForAConsumer(t *testing.T) {
|
||||
in := aGrantMesh(t)
|
||||
got := rendered(t, in, "anchor", Foreseeing)
|
||||
if len(got.Rendering.Grants) != 1 {
|
||||
t.Fatalf("grants are %+v", got.Rendering.Grants)
|
||||
}
|
||||
g := got.Rendering.Grants[0]
|
||||
if g.Provision != "s3-bucket" || g.Consumer != "laptop" || g.From != "files" || g.At != "laptop.internal" || g.Sealed != "sealed-to-anchor" {
|
||||
t.Errorf("the grant is %+v", g)
|
||||
}
|
||||
if len(got.Foreseen) != 0 || len(got.Withheld) != 0 || len(got.Unbound) != 0 {
|
||||
t.Errorf("foreseen %v withheld %v unbound %v", got.Foreseen, got.Withheld, got.Unbound)
|
||||
}
|
||||
in.Issued[0].ForProvider = ""
|
||||
got = rendered(t, in, "anchor", Foreseeing)
|
||||
if got.Rendering.Grants[0].Sealed != catalogue.ForeseenSealed || len(got.Foreseen) != 1 {
|
||||
t.Errorf("a grant with no sealed copy: %+v foreseen %v", got.Rendering.Grants, got.Foreseen)
|
||||
}
|
||||
// A credential made for a consumer on another provider is not this node's grant.
|
||||
if len(rendered(t, in, "laptop", Foreseeing).Rendering.Grants) != 0 {
|
||||
t.Error("the consumer is granted its own credential")
|
||||
}
|
||||
// A consumer whose set does not resolve is skipped, not fatal.
|
||||
nodeOf(t, &in, "laptop").Assigned = append(nodeOf(t, &in, "laptop").Assigned, "files")
|
||||
in.Catalogue["files"] = catalogue.Manifest{Module: "files", Version: "1", Requires: []string{"s3-bucket"},
|
||||
Claims: []catalogue.Claim{{Name: "x", Scope: catalogue.ScopeNode}}}
|
||||
in.Catalogue["twin"] = catalogue.Manifest{Module: "twin", Version: "1", Claims: []catalogue.Claim{{Name: "x", Scope: catalogue.ScopeNode}}}
|
||||
nodeOf(t, &in, "laptop").Assigned = append(nodeOf(t, &in, "laptop").Assigned, "twin")
|
||||
if got, err := Render(in, "anchor", Foreseeing); err != nil || len(got.Rendering.Grants) != 0 {
|
||||
t.Errorf("a consumer that does not resolve: %v %+v", err, got.Rendering.Grants)
|
||||
}
|
||||
}
|
||||
|
||||
// 1364/AC2: a consumer the grants leave out because its identity overflows the provision's bound is returned beside them, naming the
|
||||
// consumer and the provision (novox/hq ADR 0225), and is not granted.
|
||||
func TestAConsumerWhoseIdentityOverflowsIsLeftOutOfTheGrantsAndNamed(t *testing.T) {
|
||||
in := aGrantMesh(t)
|
||||
in.Nodes[1].Name = "a-machine-with-a-very-long-name"
|
||||
in.Issued[0].Consumer = "a-machine-with-a-very-long-name"
|
||||
got := rendered(t, in, "anchor", Foreseeing)
|
||||
if len(got.Rendering.Grants) != 0 || len(got.Withheld) != 1 {
|
||||
t.Fatalf("grants %+v withheld %+v", got.Rendering.Grants, got.Withheld)
|
||||
}
|
||||
w := got.Withheld[0]
|
||||
if w.Consumer != "a-machine-with-a-very-long-name" || w.Provision != "s3-bucket" || w.Provider != "anchor" || w.Module != "files" ||
|
||||
!strings.Contains(w.String(), "a-machine-with-a-very-long-name") || !strings.Contains(w.String(), "s3-bucket") {
|
||||
t.Errorf("the consumer left out is %+v / %s", w, w.String())
|
||||
}
|
||||
if len(got.Rendering.Withheld) != 1 {
|
||||
t.Errorf("the Rendering does not carry it: %+v", got.Rendering.Withheld)
|
||||
}
|
||||
}
|
||||
|
||||
// 1364/AC3: a credential on record for a consumer that its own resolution binds to another provider is withdrawn, named with where it is
|
||||
// bound (novox/hq issue 274): the grant stays, with no consumer module asking.
|
||||
func TestACredentialBoundToAnotherProviderIsWithdrawnAndSaidWhereItIsBound(t *testing.T) {
|
||||
in := aGrantMesh(t)
|
||||
second := objects()
|
||||
second.Module = "objects2"
|
||||
in.Catalogue["objects2"] = second
|
||||
in.Nodes = append(in.Nodes, Node{Name: "ace", Place: Place{Site: "there", Address: "10.77.0.3"},
|
||||
Assigned: []string{overlay.Name, "objects2"}, Capabilities: map[string]bool{"container-runtime": true, "wireguard": true, "systemd": true}})
|
||||
nodeOf(t, &in, "laptop").Pins = map[string]catalogue.Chosen{"s3-bucket": {Node: "ace", Module: "objects2"}}
|
||||
got := rendered(t, in, "anchor", Foreseeing)
|
||||
if len(got.Unbound) != 1 {
|
||||
t.Fatalf("unbound %+v grants %+v", got.Unbound, got.Rendering.Grants)
|
||||
}
|
||||
u := got.Unbound[0]
|
||||
if u.Provision != "s3-bucket" || u.Provider != "anchor" || u.Consumer != "laptop" || u.Module != "files" ||
|
||||
len(u.BoundTo) != 1 || u.BoundTo[0] != "ace" || !strings.Contains(u.String(), "ace") {
|
||||
t.Errorf("unbound is %+v / %s", u, u.String())
|
||||
}
|
||||
if len(got.Rendering.Grants) != 1 || got.Rendering.Grants[0].From != "" {
|
||||
t.Errorf("the withdrawn credential is still asked for: %+v", got.Rendering.Grants)
|
||||
}
|
||||
}
|
||||
|
||||
func aCertificateMesh(t *testing.T) Input {
|
||||
t.Helper()
|
||||
in := aMesh(t)
|
||||
in.Catalogue["secure"] = catalogue.Manifest{Module: "secure", Version: "1", Certificate: &catalogue.Certificate{Into: "/etc/secure/tls.pem", Authority: "/etc/secure/ca.pem"}}
|
||||
laptop := nodeOf(t, &in, "laptop")
|
||||
laptop.Assigned = append(laptop.Assigned, "secure")
|
||||
return in
|
||||
}
|
||||
|
||||
// 1364/AC4: with no certificate and authority in the Input a stand-in is composed and named; given them, the composition carries them as
|
||||
// given. A machine that never reported a serving key, and one not on the private network, are refused in the controller's words.
|
||||
func TestACertificateIsCarriedAsGivenOrStoodInForAndNamed(t *testing.T) {
|
||||
in := aCertificateMesh(t)
|
||||
laptop := nodeOf(t, &in, "laptop")
|
||||
if _, err := Render(in, "laptop", Foreseeing); err == nil || err.Error() != "laptop wants a certificate and has never told the mesh what key it serves with; it joins again to report one" {
|
||||
t.Fatalf("no serving key: %v", err)
|
||||
}
|
||||
laptop.ServingKey = "serving-key"
|
||||
got := rendered(t, in, "laptop", Foreseeing)
|
||||
if got.Rendering.Certificate != ForeseenCertificate || got.Rendering.Authority != ForeseenCertificate || len(got.Foreseen) != 1 {
|
||||
t.Errorf("no certificate in the Input: %q %q foreseen %v", got.Rendering.Certificate, got.Rendering.Authority, got.Foreseen)
|
||||
}
|
||||
laptop.Certificate, in.Authority = "the-certificate", "the-authority"
|
||||
got = rendered(t, in, "laptop", Foreseeing)
|
||||
if got.Rendering.Certificate != "the-certificate" || got.Rendering.Authority != "the-authority" || len(got.Foreseen) != 0 {
|
||||
t.Errorf("a certificate given: %q %q foreseen %v", got.Rendering.Certificate, got.Rendering.Authority, got.Foreseen)
|
||||
}
|
||||
laptop.Place.Address = "" // placed nowhere: no name inside the mesh
|
||||
if _, err := Render(in, "laptop", Foreseeing); err == nil || err.Error() != "laptop wants a certificate and is not on the private network, so it has no name inside the mesh to be certified for" {
|
||||
t.Fatalf("off the network: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func aValidKey(t *testing.T, name string) string { return keyOf(t, name) }
|
||||
|
||||
// 1364/AC5 and AC6: a core credential the node needs and the Input holds no box for is composed with a stand-in and returned as one
|
||||
// to keep; a kept box sealed to the key held now is carried; a node-engine below the vault-box contract is sent none (and nothing is
|
||||
// asked of it); a machine with no sealing key has its core module left out, said.
|
||||
func TestACoreBoxTheInputHoldsNoneOfIsReturnedToKeepAndNothingIsWritten(t *testing.T) {
|
||||
mk := func() Input {
|
||||
in := aMesh(t)
|
||||
in.Catalogue["nats"] = catalogue.Manifest{Module: "nats", Version: "1",
|
||||
OwnSecrets: catalogue.OwnSecrets{"broker": {Path: "/var/lib/mesh/nats/broker", Taken: catalogue.TakenAtStart}}}
|
||||
laptop := nodeOf(t, &in, "laptop")
|
||||
laptop.Assigned = append(laptop.Assigned, "nats")
|
||||
laptop.SealingKey = aValidKey(t, "laptop")
|
||||
laptop.Stated, laptop.Contract = true, VaultBoxContract
|
||||
in.BusMinted = []string{"laptop.nats"}
|
||||
return in
|
||||
}
|
||||
in := mk()
|
||||
got := rendered(t, in, "laptop", Foreseeing)
|
||||
box := got.Rendering.VaultSealed["nats"]["broker"]
|
||||
if string(box) != string(catalogue.ForeseenVaultBox) || len(got.CoreBoxesWanted) != 1 ||
|
||||
got.CoreBoxesWanted[0] != (CoreBoxWanted{Node: "laptop", Module: "nats", Name: "broker"}) {
|
||||
t.Errorf("no box held: %s wanted %v", box, got.CoreBoxesWanted)
|
||||
}
|
||||
kept := json.RawMessage(`{"v":1,"eph":"e","iv":"i","tag":"t","ct":"c"}`)
|
||||
in.KeptCoreBoxes = []CoreBox{{Node: "laptop", Module: "nats", Name: "broker", Generation: 3, SealedTo: nodeOf(t, &in, "laptop").SealingKey, Box: kept}}
|
||||
got = rendered(t, in, "laptop", Foreseeing)
|
||||
if string(got.Rendering.VaultSealed["nats"]["broker"]) != string(kept) || len(got.CoreBoxesWanted) != 0 {
|
||||
t.Errorf("a kept box: %s wanted %v", got.Rendering.VaultSealed["nats"]["broker"], got.CoreBoxesWanted)
|
||||
}
|
||||
in.KeptCoreBoxes[0].SealedTo = "an-old-key"
|
||||
if got = rendered(t, in, "laptop", Foreseeing); len(got.CoreBoxesWanted) != 1 {
|
||||
t.Errorf("a box sealed to an old key is kept: %v", got.CoreBoxesWanted)
|
||||
}
|
||||
// Below the vault-box contract: none, and the controller's own seal as before.
|
||||
nodeOf(t, &in, "laptop").Contract = VaultBoxContract - 1
|
||||
if got = rendered(t, in, "laptop", Foreseeing); got.Rendering.VaultSealed != nil || len(got.CoreBoxesWanted) != 0 {
|
||||
t.Errorf("an engine below the contract is sent boxes: %v %v", got.Rendering.VaultSealed, got.CoreBoxesWanted)
|
||||
}
|
||||
// No sealing key: the module is left out, said.
|
||||
nodeOf(t, &in, "laptop").Contract, nodeOf(t, &in, "laptop").SealingKey = VaultBoxContract, ""
|
||||
got = rendered(t, in, "laptop", Reading)
|
||||
if why := got.Rendering.NotPlaced["nats"]; !strings.Contains(why, "laptop has no sealing key, so the vault can seal nats's credentials to nothing there") {
|
||||
t.Errorf("not placed: %v", got.Rendering.NotPlaced)
|
||||
}
|
||||
unchanged(t, mk, func(in Input) { rendered(t, in, "laptop", Foreseeing) })
|
||||
}
|
||||
|
||||
// 1364/AC1: the bus's user list is carried for the machine that runs the bus and for no other; without it a stand-in is composed and
|
||||
// named; the users left out for want of a credential are said; each machine's membership is carried. (The list is composed by the
|
||||
// controller from the bus records, which stay the controller's: see the commit message.)
|
||||
func TestTheBusUserListIsCarriedForTheMachineThatRunsTheBus(t *testing.T) {
|
||||
in := aMesh(t)
|
||||
in.Catalogue["bus"] = catalogue.Manifest{Module: "bus", Version: "1", BusUsers: "/etc/bus/accounts.conf",
|
||||
Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: catalogue.ScopeMesh}}}
|
||||
anchor := nodeOf(t, &in, "anchor")
|
||||
anchor.Assigned = append(anchor.Assigned, "bus")
|
||||
in.BusUsers = "accounts { anchor.bus: hash }"
|
||||
in.BusMissing = []string{"laptop.files", "laptop.web"}
|
||||
in.BusMemberships = map[string]string{"laptop": "sealed-membership"}
|
||||
got := rendered(t, in, "anchor", Foreseeing)
|
||||
if got.Rendering.BusUsers != in.BusUsers || len(got.Foreseen) != 0 {
|
||||
t.Errorf("the list is %q foreseen %v", got.Rendering.BusUsers, got.Foreseen)
|
||||
}
|
||||
if len(got.Said) != 1 || got.Said[0] != "the bus's user list leaves out 2 user(s) the mesh has minted no credential for: laptop.files, laptop.web. Each is a user that cannot connect until one is issued" {
|
||||
t.Errorf("said %v", got.Said)
|
||||
}
|
||||
if other := rendered(t, in, "laptop", Foreseeing); other.Rendering.BusUsers != "" || other.Rendering.BusMembership != "sealed-membership" {
|
||||
t.Errorf("laptop: list %q membership %q", other.Rendering.BusUsers, other.Rendering.BusMembership)
|
||||
}
|
||||
in.BusUsers = ""
|
||||
got = rendered(t, in, "anchor", Foreseeing)
|
||||
if got.Rendering.BusUsers != ForeseenBusUsers || len(got.Foreseen) != 1 {
|
||||
t.Errorf("no list in the Input: %q foreseen %v", got.Rendering.BusUsers, got.Foreseen)
|
||||
}
|
||||
}
|
||||
|
||||
// 1364/AC7: nothing the composition says carries a secret value the Input holds.
|
||||
func TestNoSecretValueOfTheInputIsInWhatTheCompositionSays(t *testing.T) {
|
||||
in := aGrantMesh(t)
|
||||
in.Issued[0].ForProvider = secretValue
|
||||
laptop := nodeOf(t, &in, "laptop")
|
||||
in.Catalogue["assistant"] = anAssistant()
|
||||
laptop.Assigned = append(laptop.Assigned, "assistant")
|
||||
laptop.SealingKey = keyOf(t, "laptop")
|
||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "password", Origin: "made", SealedTo: laptop.SealingKey, Sealed: secretValue}}
|
||||
in.Credentials = []Credential{{Provision: "s3-bucket", Consumer: "laptop", ConsumerModule: "files", Provider: "anchor", State: CredentialHeld, Sealed: secretValue}}
|
||||
for _, node := range []string{"anchor", "laptop"} {
|
||||
got := rendered(t, in, node, Foreseeing)
|
||||
for _, said := range append(append([]string{}, got.Foreseen...), got.Said...) {
|
||||
if strings.Contains(said, secretValue) {
|
||||
t.Errorf("%s: %q carries a secret value", node, said)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user