oficina / suite Public

Clone
git clone https://amadan.net/oficina/suite

Plain git — no account needed to clone.

Download

Download this file

1package suite
2
3import (
4 "fmt"
5 "strings"
6)
7
8// SubjectKind says how to read an asker subject's value. Doctrine §3: the
9// meaning is shape-dependent and nothing else on the wire says which shape
10// the pair is in, so a receiver that does not key on the kind it is sent
11// answers UNKNOWN — loudly — rather than failing to match and answering
12// NOT_VISIBLE, which callers are entitled to act on silently.
13type SubjectKind string
14
15const (
16 // SubjectPersonRef is the assertion's opaque `sub` (home §5.3). Suite
17 // shape, where the home is the identity authority.
18 SubjectPersonRef SubjectKind = "person_ref"
19 // SubjectAddress is a lowercased address. Lone shape, where the instance
20 // keys its own members on the address it signed them in with.
21 SubjectAddress SubjectKind = "address"
22)
23
24// Subject is the named asker an S2 or S6 call carries beside its consumer
25// token. S3 carries none, by design (§6).
26type Subject struct {
27 Kind SubjectKind `json:"kind"`
28 Value string `json:"value"`
29}
30
31// NewSubject builds a subject, refusing anything a receiver could not key on
32// and normalising what it can.
33func NewSubject(kind SubjectKind, value string) (Subject, error) {
34 switch kind {
35 case SubjectPersonRef:
36 // Opaque. Case may be significant, so it is passed through untouched.
37 case SubjectAddress:
38 // Lowercased, because that is what a lone instance stores. Sending
39 // mixed case to a receiver holding the lowercased form misses, and a
40 // miss answers NOT_VISIBLE — the silent value — for what is really a
41 // normalisation bug.
42 value = strings.ToLower(value)
43 default:
44 return Subject{}, fmt.Errorf("suite: unknown asker subject kind %q", kind)
45 }
46
47 if value == "" {
48 return Subject{}, fmt.Errorf("suite: empty asker subject value for kind %q", kind)
49 }
50 return Subject{Kind: kind, Value: value}, nil
51}
52
53// MaxS2Pairs caps one S2 question. 256 is memoria §6.1's number, carried here
54// with the contract: it bounds the work a sibling can be made to do in one
55// call, and it bounds the size of a response the total decoder has to keep in
56// memory before it can answer.
57const MaxS2Pairs = 256
58
59// CheckS2Batch refuses an over-cap question rather than truncating it. A
60// silently shortened question produces a confident answer about the items
61// that survived; the ones dropped decode as UNKNOWN and nobody learns why.
62func CheckS2Batch(pairs []ItemRevision) error {
63 if len(pairs) > MaxS2Pairs {
64 return fmt.Errorf("suite: %d pairs exceeds the S2 cap of %d", len(pairs), MaxS2Pairs)
65 }
66 return nil
67}
68