carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package historyreadback
2
3import (
4 "context"
5 "os"
6 "slices"
7 "time"
8
9 "github.com/carlosframework/platform/internal/hostactors"
10 "github.com/carlosframework/platform/internal/release"
11)
12
13// ReadReconstruction verifies the exact accepted authorization from fresh,
14// enrolled-root readback. It does not certify fresh installation or retire
15// independent recovery obligations carried in the returned status.
16func ReadReconstruction(ctx context.Context, d hostactors.Directory, q Request, g release.HistoryReconstruction, now time.Time) (Receipt, error) {
17 r, err := ReadStatus(ctx, d, q, now)
18 if err != nil {
19 return Receipt{}, err
20 }
21 s := r.Status
22 accepted := digest(g)
23 if !validHex(accepted, 32) || g.Schema != 1 || g.Kind != "carlos-release-history" || g.Operation == "" || g.IssuedAt.IsZero() || g.Account != q.Scope.Binding.Account || g.App != q.Scope.Binding.App || g.Identity != s.Identity || g.Revision == 0 || g.Revision == ^uint64(0) || s.Revision != g.Revision+1 || s.Pending || s.LastReconstructionDigest != accepted || !s.AppComplete || s.AppRevision <= g.AppRevision || s.BakeComplete == 0 || s.BakeRevision <= g.BakeRevision || len(g.Hosts) == 0 || len(g.Hosts) > 256 {
24 return Receipt{}, os.ErrPermission
25 }
26 seen := map[string]bool{}
27 for _, expected := range g.Hosts {
28 if seen[expected.Host] {
29 return Receipt{}, os.ErrPermission
30 }
31 seen[expected.Host] = true
32 i := slices.IndexFunc(s.Hosts, func(h release.HistoryHostStatus) bool { return h.Host == expected.Host })
33 if i < 0 || s.Hosts[i].Source != expected.Source || s.Hosts[i].Revision <= expected.Revision || !s.Hosts[i].RefusalStatus.Complete {
34 return Receipt{}, os.ErrPermission
35 }
36 }
37 if !seen[q.Scope.Binding.Host] {
38 return Receipt{}, os.ErrPermission
39 }
40 return r, nil
41}
42
43// ReadRetirement verifies one effective retirement. Unrelated restrictions
44// remain in the receipt and must not be treated as cleared by this result.
45func ReadRetirement(ctx context.Context, d hostactors.Directory, q Request, g release.HistoryRecoveryRetirement, now time.Time) (Receipt, error) {
46 r, err := ReadStatus(ctx, d, q, now)
47 if err != nil {
48 return Receipt{}, err
49 }
50 s := r.Status
51 accepted := digest(g)
52 if !validHex(accepted, 32) || g.Schema != 1 || g.Kind != "carlos-release-recovery-retirement" || g.Operation == "" || g.IssuedAt.IsZero() || g.Binding != q.Scope.Binding || g.Identity != s.Identity || s.Pending || s.RecoverySources == nil || !s.RecoverySources.Known || !validHex(g.OutcomeDigest, 32) || !validHex(g.VerifiedDigest, 32) || g.AttemptID == "" || g.VerifiedID == "" || g.AttemptID == g.VerifiedID {
53 return Receipt{}, os.ErrPermission
54 }
55 for _, pending := range s.RecoverySources.Unresolved {
56 if pending.Binding == g.Binding && (pending.Attempt.ID == g.AttemptID || pending.Attempt.ID == g.VerifiedID) {
57 return Receipt{}, os.ErrPermission
58 }
59 }
60 for _, h := range s.Hosts {
61 if h.Host == g.Binding.Host && h.Source == g.Source && slices.Contains(h.RetiredRecoveries, g.AttemptID) && h.RetirementDigests[g.AttemptID] == accepted && h.RecoveryDigests[g.AttemptID] == g.OutcomeDigest && h.RecoveryDigests[g.VerifiedID] == g.VerifiedDigest {
62 return r, nil
63 }
64 }
65 return Receipt{}, os.ErrPermission
66}
67