Give the controller the contract it can fill (hq ADR 0325, issue 567)
The controller cannot know the operator's account's uid, names an access by its declared id (which holds a slash), and should not parse words. A tool runner's account may now come by name alone and is read here (refused when absent or root's, its uid said in the access's listing); the take act matches the declared id exactly; an access wait says its reason (keep or disk) and, for a filesystem that refuses ACLs, whether the take act can enable them.
This commit is contained in:
+64
-3
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/novox/mesh-host/internal/accounts"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -476,6 +477,10 @@ func RootWords(why string) string {
|
||||
// Nothing is changed on the folder but the entries the mesh adds and the mask that shows them.
|
||||
func applyAccess(ctx context.Context, sys system.System, r *declaration.Access, run Runner, previous store.Applied,
|
||||
in inputs) (Outcome, error) {
|
||||
// A tool runner's account that is not here, or is root's, refuses its access (§4, §11).
|
||||
if err := in.runners[r.ID]; err != nil {
|
||||
return begin(r), err
|
||||
}
|
||||
// What ADR 0312's grant put an account in is taken back first: no account is in a group for data (§5).
|
||||
gave, left, said := takeBackGranted(ctx, sys, previous, run, in.groups, in.handed)
|
||||
out, err := applyAccessFolder(ctx, r, in)
|
||||
@@ -832,10 +837,18 @@ const (
|
||||
VerdictNobody = "nobody"
|
||||
)
|
||||
|
||||
// What a keep wait says of its folder's filesystem and ACLs: the same words as link's ACLNeedsEnabling and
|
||||
// ACLUnsupported, which the engine says them as.
|
||||
const (
|
||||
ACLNeedsEnabling = "needs-enabling"
|
||||
ACLUnsupported = "unsupported"
|
||||
)
|
||||
|
||||
// AccessJudgement is a look's judgement of an access's folder, and why, in words; Placed names the filesystem at
|
||||
// it for a wait, with no path (PlacedID).
|
||||
// it for a wait, with no path (PlacedID); ACL is, on a keep wait, ACLNeedsEnabling or ACLUnsupported
|
||||
// when its filesystem refuses access entries now.
|
||||
type AccessJudgement struct {
|
||||
Kind, Why, Placed string
|
||||
Kind, Why, Placed, ACL string
|
||||
}
|
||||
|
||||
// AccessVerdict judges an access's folder for a look, read only (ADR 0325 §7, §9, §12): taken is whether the
|
||||
@@ -858,7 +871,7 @@ func AccessVerdict(ctx context.Context, path string, taken, used bool) AccessJud
|
||||
case v.kind == folderMake:
|
||||
return AccessJudgement{Kind: VerdictMake}
|
||||
case v.kind == folderWaitKeep, v.kind == folderKeep && !rec.Kept && !taken:
|
||||
return AccessJudgement{Kind: VerdictWaitKeep, Placed: PlacedID(v.mount)}
|
||||
return AccessJudgement{Kind: VerdictWaitKeep, Placed: PlacedID(v.mount), ACL: aclProbe(path, v.mount)}
|
||||
}
|
||||
return AccessJudgement{Kind: VerdictKeep}
|
||||
}
|
||||
@@ -951,3 +964,51 @@ func legacyAccess(r *declaration.Access) (Outcome, error) {
|
||||
out.Detail = "operator-owned; present, " + mode + ", nothing managed"
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AccountIDs reads an account's uid and primary gid by name from the machine's user database (getent, through the
|
||||
// account judge's reader). A variable so a test can stand in for accounts it cannot make.
|
||||
var AccountIDs = func(ctx context.Context, name string) (uid, gid int, found bool, err error) {
|
||||
return accounts.Exec{Run: accounts.Runner(ExecRunner)}.IDs(ctx, name)
|
||||
}
|
||||
|
||||
// ResolveRunners gives every tool runner's account that came with no uid its number, read by name on this machine
|
||||
// (ADR 0325 §4): the controller cannot know the operator's account's. One that is not here, or is root's — uid 0 or
|
||||
// a root group (§11) — is taken out of the access's users, and its access is refused, naming why, by id. Changes
|
||||
// the declaration in place; a user that already has its number is left as it is.
|
||||
func ResolveRunners(ctx context.Context, d *declaration.Declaration) map[string]error {
|
||||
refused := map[string]error{}
|
||||
if d == nil {
|
||||
return refused
|
||||
}
|
||||
for _, r := range d.Resources {
|
||||
a, ok := r.(*declaration.Access)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
kept := a.Users[:0]
|
||||
for _, u := range a.Users {
|
||||
if u.From != declaration.FromToolRunner || u.UID != 0 {
|
||||
kept = append(kept, u)
|
||||
continue
|
||||
}
|
||||
uid, gid, found, err := AccountIDs(ctx, u.Identity)
|
||||
switch {
|
||||
case err != nil:
|
||||
refused[a.ID] = fmt.Errorf("the tool runner's account %s could not be read: %w", u.Identity, err)
|
||||
continue
|
||||
case !found:
|
||||
refused[a.ID] = fmt.Errorf("the tool runner's account %s is not on this machine, so no entry can "+
|
||||
"be kept for it (novox/hq ADR 0325 §4)", u.Identity)
|
||||
continue
|
||||
}
|
||||
if err := rootIdentity(uid, gid); err != nil {
|
||||
refused[a.ID] = fmt.Errorf("the tool runner's account %s is never given an entry: %w", u.Identity, err)
|
||||
continue
|
||||
}
|
||||
u.UID = uid
|
||||
kept = append(kept, u)
|
||||
}
|
||||
a.Users = kept
|
||||
}
|
||||
return refused
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user