A module's containers ran as whatever their image chose and shared folders were made writable by putting accounts in the folder's group (ADR 0312), which ADR 0325 supersedes. The engine now makes each declared module identity as a system account with the controller's numbers, runs a container declaring `identity` as it, and keeps each placed shared folder usable through one POSIX ACL entry per user, set through the kernel's xattrs on descriptors it opened itself. A folder holding data is kept only after the operator's warrant (the take act); a missing folder is made where its data lives and waits where its disk is not; root's data is never kept. A watch and a bounded sweep put back what an app narrows. The group grant is removed, and what it put an account in is taken back. Reuses the descriptor walks, the create act, the missing-path wait and the warrant- backed act message from the paused issue 567 branch.
69 lines
3.0 KiB
Go
69 lines
3.0 KiB
Go
package link
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// The take act's ask (novox/hq ADR 0325 §8): a hand-over's fields and the act's own in one signed object, under a
|
|
// signing context of its own. The controller's side holds the same names and words.
|
|
func TestTheTakeActsAskKeepsItsFieldNamesAndContext(t *testing.T) {
|
|
body, _ := json.Marshal(AccessActAsk{Act: ActTake, Access: "radarr.movies", Warrant: "w"})
|
|
if got := keysOf(t, body); got != "access act warrant" {
|
|
t.Fatalf("the act's fields are %q", got)
|
|
}
|
|
if AskAccessActSubject("ace") != "mesh.node.ace.ask.access-act" {
|
|
t.Fatalf("the subject is %q", AskAccessActSubject("ace"))
|
|
}
|
|
if AccessActContext != "novox-mesh access-act v2\n" {
|
|
t.Fatalf("the signing context is %q", AccessActContext)
|
|
}
|
|
}
|
|
|
|
// signedAct is an act's ask as the controller makes it: the hand-over's fields beside the act's, signed over the
|
|
// act's context and its bytes.
|
|
func signedAct(t *testing.T, key ed25519.PrivateKey, ask HandOverAsk, act AccessActAsk) []byte {
|
|
t.Helper()
|
|
raw, _ := json.Marshal(struct {
|
|
HandOverAsk
|
|
AccessActAsk
|
|
}{ask, act})
|
|
body, _ := json.Marshal(SignedHandOver{Ask: raw, Signature: ed25519.Sign(key, append([]byte(AccessActContext), raw...))})
|
|
return body
|
|
}
|
|
|
|
// **Nothing is done without a warrant**, nor for any act but take, nor signed as a hand-over (ADR 0325 §8).
|
|
func TestTheTakeActRunsOnlyOnAWarrant(t *testing.T) {
|
|
m, key := aMember(t)
|
|
now := time.Now()
|
|
var given []AccessAct
|
|
handle := func(a AccessAct) (string, error) { given = append(given, a); return "started", nil }
|
|
check := HandOverCheck{Signer: m.Signer, Node: m.Node, Nonces: &Nonces{}}
|
|
ask := func(nonce string) HandOverAsk {
|
|
return HandOverAsk{Node: m.Node, Path: "/srv/media/movies", By: "the operator", Expires: now.Add(HandOverGood),
|
|
Nonce: nonce}
|
|
}
|
|
for why, body := range map[string][]byte{
|
|
"no warrant": signedAct(t, key, ask(aNonce+"01"), AccessActAsk{Act: ActTake, Access: "radarr.movies"}),
|
|
"another act": signedAct(t, key, ask(aNonce+"02"), AccessActAsk{Act: "share", Access: "radarr.movies", Warrant: "w"}),
|
|
"no access": signedAct(t, key, ask(aNonce+"03"), AccessActAsk{Act: ActTake, Warrant: "w"}),
|
|
"a hand-over's": signedAsk(t, key, ask(aNonce+"04")),
|
|
"a path as access": signedAct(t, key, ask(aNonce+"05"), AccessActAsk{Act: ActTake, Access: "/srv", Warrant: "w"}),
|
|
} {
|
|
if a := AnswerAccessAct(check, handle, body); a.Refused == "" || !strings.HasSuffix(a.Refused, "nothing was changed") {
|
|
t.Errorf("%s: not refused, or refused without saying nothing was changed: %+v", why, a)
|
|
}
|
|
}
|
|
if len(given) != 0 {
|
|
t.Fatalf("a refused ask reached the act: %+v", given)
|
|
}
|
|
a := AnswerAccessAct(check, handle, signedAct(t, key, ask(aNonce+"06"), AccessActAsk{Act: ActTake,
|
|
Access: "radarr.movies", Warrant: "w-1"}))
|
|
if a.Said != "started" || len(given) != 1 || given[0].Warrant != "w-1" || given[0].Path != "/srv/media/movies" {
|
|
t.Fatalf("a warranted ask did not reach the act whole: %+v %+v", a, given)
|
|
}
|
|
}
|