A tool's grant covers the machine-addressed subject, and a credential names the seats its module claims (ADR 0159) #185
@@ -584,16 +584,25 @@ func issueOnTheNewBus(ctx context.Context, inv *inventory.Inventory, m catalogue
|
|||||||
// (`rollout mint`, design 28 task 5.2) rather than the one in this process's environment.
|
// (`rollout mint`, design 28 task 5.2) rather than the one in this process's environment.
|
||||||
func issueWith(ctx context.Context, inv *inventory.Inventory, m catalogue.Manifest,
|
func issueWith(ctx context.Context, inv *inventory.Inventory, m catalogue.Manifest,
|
||||||
node, busAddress string, known broker.Broker, reachable, user, password string) error {
|
node, busAddress string, known broker.Broker, reachable, user, password string) error {
|
||||||
|
// The seats this module claims, with the verbs each promises (novox/hq ADR 0159): the runtime
|
||||||
|
// serves a claimed seat's verbs with its tools of the same name, and the bus admits only the
|
||||||
|
// holder's subscription — so the runtime tries each claim and the grant decides. Written here
|
||||||
|
// because this file is the one thing the mesh writes that the runtime reads before it speaks.
|
||||||
|
claims, err := claimsFor(ctx, inv, m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
held, err := json.Marshal(struct {
|
held, err := json.Marshal(struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Fingerprint string `json:"fingerprint,omitempty"`
|
Fingerprint string `json:"fingerprint,omitempty"`
|
||||||
Node string `json:"node"`
|
Node string `json:"node"`
|
||||||
Module string `json:"module"`
|
Module string `json:"module"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
Claims []seatClaimed `json:"claims,omitempty"`
|
||||||
}{
|
}{
|
||||||
URL: "nats://" + reachable, Fingerprint: known.Fingerprint,
|
URL: "nats://" + reachable, Fingerprint: known.Fingerprint,
|
||||||
Node: node, Module: m.Module, User: user, Password: password,
|
Node: node, Module: m.Module, User: user, Password: password, Claims: claims,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -680,3 +689,36 @@ func namesNoInstallation(m catalogue.Manifest) error {
|
|||||||
"on purpose under %s with its reason, or take it out:\n - %s",
|
"on purpose under %s with its reason, or take it out:\n - %s",
|
||||||
m.Module, catalogue.NamesOnPurpose, strings.Join(named, "\n - "))
|
m.Module, catalogue.NamesOnPurpose, strings.Join(named, "\n - "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// seatClaimed is one seat a module claims, as its runtime needs it: the name, the scope (a
|
||||||
|
// node-scoped seat's verb carries the machine, design 33 §4) and the verbs the seat promises.
|
||||||
|
type seatClaimed struct {
|
||||||
|
Seat string `json:"seat"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
Serves []string `json:"serves,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// claimsFor joins a module's claims with the seats' protocols from the mesh's records.
|
||||||
|
func claimsFor(ctx context.Context, inv *inventory.Inventory, m catalogue.Manifest) ([]seatClaimed, error) {
|
||||||
|
if len(m.Claims) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
seats, err := inv.Seats(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
byName := map[string]catalogue.Seat{}
|
||||||
|
for _, s := range seats {
|
||||||
|
byName[s.Name] = s
|
||||||
|
}
|
||||||
|
var out []seatClaimed
|
||||||
|
for _, c := range m.Claims {
|
||||||
|
claimed := seatClaimed{Seat: c.Name, Scope: c.At()}
|
||||||
|
if s, known := byName[c.Name]; known {
|
||||||
|
claimed.Scope = s.Scope
|
||||||
|
claimed.Serves = catalogue.VerbNames(s.Serves)
|
||||||
|
}
|
||||||
|
out = append(out, claimed)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -91,3 +91,16 @@ func TestADeclaredInvokeReachesTheComposedUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
has(t, perms.Publish, "mesh.mod.*.tool.>")
|
has(t, perms.Publish, "mesh.mod.*.tool.>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A module's tool is addressed two ways (novox/hq ADR 0159): to whichever instance answers, and to
|
||||||
|
// the instance on one machine. A grant for the tool covers both and nothing wider.
|
||||||
|
func TestInvokingAToolMayAddressTheMachineToo(t *testing.T) {
|
||||||
|
got, err := invokedSubjects([]string{"postgres.postgres_query"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
want := []string{"mesh.mod.postgres.tool.postgres_query", "mesh.mod.postgres.tool.postgres_query.*"}
|
||||||
|
if len(got) != 2 || got[0] != want[0] || got[1] != want[1] {
|
||||||
|
t.Fatalf("the grant is %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -660,7 +660,10 @@ func invokedSubjects(invokes []string) ([]string, error) {
|
|||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"%q does not name a tool: one invokes <module>.<tool>, seat:<seat>.<verb>, or * for every one", t)
|
"%q does not name a tool: one invokes <module>.<tool>, seat:<seat>.<verb>, or * for every one", t)
|
||||||
}
|
}
|
||||||
out = append(out, "mesh.mod."+module+".tool."+tool)
|
// Both ways a module's tool is addressed (novox/hq ADR 0159): to whichever instance
|
||||||
|
// answers, and to the instance on one machine, which is the same subject with the machine
|
||||||
|
// as its last token.
|
||||||
|
out = append(out, "mesh.mod."+module+".tool."+tool, "mesh.mod."+module+".tool."+tool+".*")
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user