Repository navigation
Expand file tree
/
Copy pathgrants.go
More file actions
87 lines (79 loc) · 3.46 KB
/
Copy pathgrants.go
File metadata and controls
87 lines (79 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package addon
import (
"slices"
"strings"
"github.com/DevOfPie/LinkCtrl/internal/addon/abi"
)
// Grants is the permission set one add-on holds, resolved once at load.
//
// **Resolved once, and the shape is load-bearing.** From M66 a grant check sits
// on the redirect path, where the inherited rule is a cached p99 under 20 ms, so
// [Grants.Has] has to be a lookup on an already-resolved set — never a read of
// the manifest, never a walk of the vocabulary, and never I/O. A test asserts it
// allocates nothing, and another asserts that editing a manifest's Permissions
// slice after load does not change what the add-on holds, because that is the
// falsifiable form of *resolved once*.
//
// The zero value is a valid empty grant set, which is what an add-on that
// declared nothing holds. Every method is therefore safe on it.
type Grants struct {
// held is the set, not a slice, for the reason above. Unexported and never
// mutated after resolveGrants returns, which is what makes copying a Grants —
// as Addons() copies a Loaded — safe without a lock.
held map[string]struct{}
}
// Has reports whether this add-on holds the named permission.
func (g Grants) Has(permission string) bool {
_, ok := g.held[permission]
return ok
}
// Len is how many grants are held.
func (g Grants) Len() int { return len(g.held) }
// Names is the held set, sorted, which is what a log line and a metric label
// carry. Sorted so that two boots of one instance produce the same string and an
// operator diffing them sees only real changes.
func (g Grants) Names() []string {
out := make([]string, 0, len(g.held))
for name := range g.held {
out = append(out, name)
}
slices.Sort(out)
return out
}
// String is the label form: the sorted names, comma-separated, and empty for an
// add-on that holds nothing.
func (g Grants) String() string { return strings.Join(g.Names(), ",") }
// resolveGrants turns a manifest's declarations into what the add-on actually
// holds, plus the declarations that were **withheld**.
//
// Withheld means declared, in the vocabulary, and not grantable on this host.
// **Nothing is withheld today**: `redirect.inline` was, from M62 until M66 turned
// it on, and it is what the shape was built for. It is not an error and does not
// refuse the add-on — a class exists in the vocabulary so that a later milestone
// can turn it on, and refusing a module for declaring it would make the
// declaration unusable. The module simply does not hold it, every capability
// behind it is denied, and the host says so at load.
//
// The path is kept rather than deleted with its last user, because the ordering it
// buys is what made M66 cheap: a grant declared and enforced four milestones
// before the behaviour landed meant the behaviour landed against enforcement that
// already worked. The next class to be planned ahead of its milestone uses this
// unchanged.
//
// A token *outside* the vocabulary never reaches here. Manifest.Validate refuses
// it, for the reason DisallowUnknownFields refuses an unknown field: a
// declaration this host cannot interpret is a manifest whose author expects
// behaviour that will not happen, and the vocabulary is closed.
func resolveGrants(m Manifest) (Grants, []string) {
g := Grants{held: make(map[string]struct{}, len(m.Permissions))}
var withheld []string
for _, p := range m.Permissions {
if abi.Grantable(p) {
g.held[p] = struct{}{}
continue
}
withheld = append(withheld, p)
}
slices.Sort(withheld)
return g, withheld
}