Let a trusted file that names its keys hold only those keys at the terminal (hq issue 586, ADR 0329 §5)
mesh/merge-gate head 3275a16bebbb merged into trunk 584ee991961c — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 3275a16bebbb merged into trunk 584ee991961c — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/merge-gate head 3275a16bebbb merged into trunk 584ee991961c — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 3275a16bebbb merged into trunk 584ee991961c — pass: its merge-check.sh passed
mesh/delivery delivered
A module with a trusted mergeable file had its whole layer refused through the settings verb, because such a file took any key. Once a module's files name the keys they take (ADR 0326), each file takes only those, so only a trusted file's named keys are the terminal's: betterbird's tags, in a file nothing trusts, is set through the verb, while its accounts and identities stay the terminal's or the operator's warrant. A module whose files name no keys keeps the whole-layer rule.
This commit is contained in:
@@ -345,6 +345,37 @@ func TestAnyKeyOfAModuleWithATrustedMergeableFileIsRefusedThroughAVerb(t *testin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A module whose mergeable files name the keys they take (ADR 0326): a key of a trusted file is refused through a
|
||||||
|
// verb, and a key of a file nothing trusts is set through it (novox/hq ADR 0329 §5, issue 586: betterbird's tags).
|
||||||
|
func TestOnlyATrustedFilesNamedKeyIsRefusedThroughAVerb(t *testing.T) {
|
||||||
|
open := aMesh(t)
|
||||||
|
ctx := t.Context()
|
||||||
|
register(t, open, catalogue.Manifest{Module: "mailer", Version: "1",
|
||||||
|
Resources: []map[string]any{
|
||||||
|
{"id": "accounts", "type": "file", "path": "/var/lib/mailer/accounts.json", "mode": "0600", "merge": "json",
|
||||||
|
"trusted": true, "takes-settings": []any{"accounts"}, "content": `{"accounts": {}}`},
|
||||||
|
{"id": "tags", "type": "file", "path": "/var/lib/mailer/tags.json", "mode": "0644", "merge": "json",
|
||||||
|
"trusted": false, "takes-settings": []any{"tags"}, "content": `{"tags": {}}`}}})
|
||||||
|
if _, err := assign(ctx, open, "anchor", "mailer"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := throughVerb(t, "settings", map[string]any{"module": "mailer", "values": `{"tags":{"todo":{"name":"To do"}}}`}); err != nil {
|
||||||
|
t.Fatalf("a key of a file nothing trusts is the verb's: %v", err)
|
||||||
|
}
|
||||||
|
err := throughVerb(t, "settings", map[string]any{"module": "mailer", "replace": "true",
|
||||||
|
"values": `{"tags":{"todo":{"name":"To do"}},"accounts":{"mail":{"address":"me@mail.example"}}}`})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "controller's terminal") || !strings.Contains(err.Error(), "accounts") {
|
||||||
|
t.Fatalf("a key of a trusted file is the terminal's: %v", err)
|
||||||
|
}
|
||||||
|
values, _, err := open.inventory.Layer(ctx, "", "mailer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if raw, _ := json.Marshal(values); string(raw) != `{"tags":{"todo":{"name":"To do"}}}` {
|
||||||
|
t.Fatalf("the layer holds the tags alone: %s", raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Who owns a module's directory is said at the terminal alone (novox/hq issue 279): a directory's owner may name
|
// Who owns a module's directory is said at the terminal alone (novox/hq issue 279): a directory's owner may name
|
||||||
// the settings the module runs as, root chowns the folder to them at the next push, and through the settings verb
|
// the settings the module runs as, root chowns the folder to them at the next push, and through the settings verb
|
||||||
// any caller could have set puid to 0 or to its own uid. The key is refused through every verb route even where a
|
// any caller could have set puid to 0 or to its own uid. The key is refused through every verb route even where a
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ import (
|
|||||||
// 4. **A module's whole layer, when it has a mergeable file not marked `"trusted": false`** (novox/hq issue 340,
|
// 4. **A module's whole layer, when it has a mergeable file not marked `"trusted": false`** (novox/hq issue 340,
|
||||||
// TrustedMergeable). A mergeable file takes any key a layer sets, not only those its content names, so no list
|
// TrustedMergeable). A mergeable file takes any key a layer sets, not only those its content names, so no list
|
||||||
// of keys covers it: an empty runtime configuration a provider reads would take a `url` of the caller's.
|
// of keys covers it: an empty runtime configuration a provider reads would take a `url` of the caller's.
|
||||||
|
// **Unless the module's files name the keys they take** (novox/hq ADR 0326, ADR 0329 §5): then a mergeable
|
||||||
|
// file takes exactly its `takes-settings` and no other key, so those keys of a trusted file are terminal keys
|
||||||
|
// and the rest of the layer is not. A key no file names is stray and refused where it is set. So betterbird's
|
||||||
|
// `tags`, in a file nothing trusts, is set through the verb, while its `accounts` and `identities` are not.
|
||||||
// 5. **Every setting a directory's owner asks for** (novox/hq issue 279): root chowns the directory to the number
|
// 5. **Every setting a directory's owner asks for** (novox/hq issue 279): root chowns the directory to the number
|
||||||
// at the next push, so through a verb any caller could hand itself — or uid 0 — a module's folder. Whatever
|
// at the next push, so through a verb any caller could hand itself — or uid 0 — a module's folder. Whatever
|
||||||
// else asks for the key, a file marked `"trusted": false` included, the owner makes it the terminal's.
|
// else asks for the key, a file marked `"trusted": false` included, the owner makes it the terminal's.
|
||||||
@@ -109,8 +113,13 @@ func UnsaidTrust(m Manifest) []string {
|
|||||||
// TrustedMergeable is every mergeable file of a module not marked `"trusted": false`, by id (novox/hq issue 340).
|
// TrustedMergeable is every mergeable file of a module not marked `"trusted": false`, by id (novox/hq issue 340).
|
||||||
// A mergeable file takes any key a layer sets, not only those its content names: an empty runtime configuration a
|
// A mergeable file takes any key a layer sets, not only those its content names: an empty runtime configuration a
|
||||||
// provider reads takes a `url` of a caller's as surely as a declared one. So a module holding one has its whole
|
// provider reads takes a `url` of a caller's as surely as a declared one. So a module holding one has its whole
|
||||||
// layer set at the controller's terminal; a verb may read it and change nothing.
|
// layer set at the controller's terminal; a verb may read it and change nothing. A module whose mergeable files
|
||||||
|
// name the keys they take (ADR 0326) has none here: each such file takes only its named keys, which TerminalKeys
|
||||||
|
// holds for a trusted one (ADR 0329 §5).
|
||||||
func TrustedMergeable(m Manifest) []string {
|
func TrustedMergeable(m Manifest) []string {
|
||||||
|
if namesItsKeys(m) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var out []string
|
var out []string
|
||||||
for _, r := range m.Resources {
|
for _, r := range m.Resources {
|
||||||
if fmt.Sprint(r["type"]) != "file" {
|
if fmt.Sprint(r["type"]) != "file" {
|
||||||
@@ -135,6 +144,16 @@ func TrustedMergeable(m Manifest) []string {
|
|||||||
func asksFor(r map[string]any) []string {
|
func asksFor(r map[string]any) []string {
|
||||||
content, _ := r["content"].(string)
|
content, _ := r["content"].(string)
|
||||||
asked := settingsUsed(content)
|
asked := settingsUsed(content)
|
||||||
|
if how, _ := r["merge"].(string); how != "" {
|
||||||
|
if taken, names := takenKeys(r); names {
|
||||||
|
// The file takes exactly the keys it names (ADR 0326), whatever its content holds.
|
||||||
|
for key := range taken {
|
||||||
|
asked = append(asked, key)
|
||||||
|
}
|
||||||
|
sort.Strings(asked)
|
||||||
|
return asked
|
||||||
|
}
|
||||||
|
}
|
||||||
if how, _ := r["merge"].(string); how != "" && strings.TrimSpace(content) != "" {
|
if how, _ := r["merge"].(string); how != "" && strings.TrimSpace(content) != "" {
|
||||||
var base map[string]any
|
var base map[string]any
|
||||||
if json.Unmarshal([]byte(content), &base) == nil {
|
if json.Unmarshal([]byte(content), &base) == nil {
|
||||||
|
|||||||
@@ -166,6 +166,33 @@ func TestTerminalKeysAreDerived(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A module whose mergeable files name the keys they take (ADR 0326) has no whole layer at the terminal: a
|
||||||
|
// trusted file's named keys are terminal keys, and a file nothing trusts leaves its keys to the verb (novox/hq
|
||||||
|
// ADR 0329 §5, issue 586: betterbird's tags beside its trusted accounts, identities, calendars, address books
|
||||||
|
// and filters). A module whose files name no keys keeps the whole layer at the terminal (issue 340).
|
||||||
|
func TestOnlyANamedKeyOfATrustedFileIsTheTerminals(t *testing.T) {
|
||||||
|
betterbird := Manifest{Module: "betterbird", Resources: []map[string]any{
|
||||||
|
{"id": "prefs", "type": "file", "path": "/home/p/prefs.json", "merge": MergeJSON, "trusted": true,
|
||||||
|
TakesSettingsField: []any{"prefs"}, "content": `{"prefs": {}}`},
|
||||||
|
{"id": "accounts", "type": "file", "path": "/home/p/accounts.json", "merge": MergeJSON, "trusted": true,
|
||||||
|
TakesSettingsField: []any{"accounts"}, "content": `{"accounts": {}}`},
|
||||||
|
{"id": "configuration", "type": "file", "path": "/home/p/configuration.json", "merge": MergeJSON,
|
||||||
|
TakesSettingsField: []any{"identities", "calendars", "address-books", "filters"}, "content": `{}`},
|
||||||
|
{"id": "tags", "type": "file", "path": "/home/p/tags.json", "merge": MergeJSON, "trusted": false,
|
||||||
|
TakesSettingsField: []any{"tags"}, "content": `{"tags": {}}`}}}
|
||||||
|
if got := TrustedMergeable(betterbird); len(got) != 0 {
|
||||||
|
t.Fatalf("files that name their keys put no whole layer at the terminal: %v", got)
|
||||||
|
}
|
||||||
|
if got := strings.Join(TerminalKeys(betterbird), ","); got != "places,accesses,accounts,address-books,calendars,filters,identities,prefs" {
|
||||||
|
t.Fatalf("the named keys of the trusted files, unmarked counting as trusted, and not tags: %s", got)
|
||||||
|
}
|
||||||
|
silent := Manifest{Module: "keycloak", Resources: []map[string]any{{"id": "runtime", "type": "file",
|
||||||
|
"path": "/var/lib/kc/runtime.json", "merge": MergeJSON, "content": `{}`}}}
|
||||||
|
if got := TrustedMergeable(silent); len(got) != 1 {
|
||||||
|
t.Fatalf("a file that names no keys still takes any key, so its module's layer stays the terminal's: %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A file that asks for a setting says whether what it asks is trusted (novox/hq issue 339): `trusted` is a
|
// A file that asks for a setting says whether what it asks is trusted (novox/hq issue 339): `trusted` is a
|
||||||
// boolean, on a file alone, and a file asking for a setting without it is named.
|
// boolean, on a file alone, and a file asking for a setting without it is named.
|
||||||
func TestAFileSaysWhetherItsSettingsAreTrusted(t *testing.T) {
|
func TestAFileSaysWhetherItsSettingsAreTrusted(t *testing.T) {
|
||||||
|
|||||||
Vendored
+1
@@ -40,3 +40,4 @@
|
|||||||
./internal/inventory TestAStoredBuildWithASilentMergeableFileIsStillReadAndComposed
|
./internal/inventory TestAStoredBuildWithASilentMergeableFileIsStillReadAndComposed
|
||||||
./cmd/mesh-controller TestPlanAndNodeListAnOptionalRequirementLeftUnbound
|
./cmd/mesh-controller TestPlanAndNodeListAnOptionalRequirementLeftUnbound
|
||||||
./cmd/mesh-controller TestReplay340
|
./cmd/mesh-controller TestReplay340
|
||||||
|
./cmd/mesh-controller TestOnlyATrustedFilesNamedKeyIsRefusedThroughAVerb
|
||||||
|
|||||||
Reference in New Issue
Block a user