carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "crypto/rand"
6 "encoding/hex"
7 "os"
8 "slices"
9 "time"
10
11 "github.com/carlosframework/platform/internal/hostwrite"
12 "github.com/carlosframework/platform/internal/registry"
13)
14
15type RootHistoryPass struct {
16 Creation func(context.Context, registry.Route) (string, error)
17 History *RootHistory
18 Resolve func(context.Context, registry.Route) (HistoryBinding, string, error)
19 Installed func(context.Context, registry.Route, string) (string, error)
20 Prepare func(context.Context, HistoryBinding, HistoryAttempt, hostwrite.Identity) error
21 Reconstruct func(context.Context, HistoryBinding) error
22}
23
24type historySelection struct {
25 binding HistoryBinding
26 source string
27 creation string
28}
29
30func WithRecovery(fn func(context.Context, registry.Route) (bool, error), legacy ...func() (bool, error)) PassOption {
31 return func(c *passConfig) {
32 c.recovery = fn
33 if len(legacy) == 1 {
34 c.legacyRecovery = legacy[0]
35 }
36 }
37}
38
39func WithRootHistory(h *RootHistoryPass) PassOption {
40 return func(c *passConfig) {
41 c.rootHistory = h
42 c.requireRootHistory = true
43 }
44}
45
46func (h *RootHistoryPass) resolve(ctx context.Context, rt registry.Route) (historySelection, error) {
47 if h == nil || h.History == nil || h.Resolve == nil {
48 return historySelection{}, os.ErrPermission
49 }
50 b, source, err := h.Resolve(ctx, rt)
51 if err != nil {
52 return historySelection{}, err
53 }
54 if !b.valid() || source == "" || b.Host != rt.Host || b.Account != RouteAccount(rt) || b.App != rt.App {
55 return historySelection{}, os.ErrPermission
56 }
57 creation := ""
58 if h.Creation != nil {
59 creation, err = h.Creation(ctx, rt)
60 if err != nil {
61 return historySelection{}, err
62 }
63 }
64 return historySelection{binding: b, source: source, creation: creation}, nil
65}
66
67func (h *RootHistoryPass) selectRoute(ctx context.Context, rt registry.Route) (historySelection, error) {
68 sel, err := h.resolve(ctx, rt)
69 if err != nil {
70 return sel, err
71 }
72 if h.Installed == nil {
73 return sel, os.ErrPermission
74 }
75 if h.Reconstruct != nil {
76 if err := h.Reconstruct(ctx, sel.binding); err != nil {
77 return sel, err
78 }
79 if err := h.recheck(ctx, rt, sel); err != nil {
80 return sel, err
81 }
82 }
83 if _, err := h.History.Programs(sel.binding); err != nil {
84 return sel, err
85 }
86 _, err = h.History.Refusals(sel.binding)
87 return sel, err
88}
89
90func (c passConfig) recheckHistory(ctx context.Context, rt registry.Route) error {
91 if !c.requireRootHistory {
92 return nil
93 }
94 return c.rootHistory.recheck(ctx, rt, c.historySelected[rt.Host])
95}
96
97func (c passConfig) bindProof(ctx context.Context, optDir string, rt registry.Route, ptr *ChannelPointer, v proofVerdict, cur string, now time.Time) bool {
98 if !c.requireRootHistory {
99 return c.proof.bind(optDir, rt, ptr, v, cur, now)
100 }
101 return c.proof.bindRecording(optDir, rt, ptr, v, cur, now, func(r Refusal) error {
102 err := c.recordRefusal(ctx, rt, r)
103 c.historyError(err)
104 return err
105 })
106}
107
108func (h *RootHistoryPass) recheck(ctx context.Context, rt registry.Route, want historySelection) error {
109 got, err := h.resolve(ctx, rt)
110 if err != nil {
111 return err
112 }
113 if got != want {
114 return os.ErrPermission
115 }
116 return nil
117}
118
119func (h *RootHistoryPass) begin(ctx context.Context, rt registry.Route, sel historySelection, ptr *ChannelPointer, digest string, now time.Time, identity hostwrite.Identity) (string, error) {
120 if err := h.recheck(ctx, rt, sel); err != nil {
121 return "", err
122 }
123 var id [16]byte
124 if _, err := rand.Read(id[:]); err != nil {
125 return "", err
126 }
127 a := HistoryAttempt{ID: hex.EncodeToString(id[:]), Version: ptr.Version, Source: sel.source, Started: now.UTC(), SHA256: digest, Platform: adoptPlatform, PromotedAt: ptr.PromotedAt}
128 if err := h.History.Begin(sel.binding, a); err != nil {
129 return "", err
130 }
131 if h.Prepare != nil {
132 if err := h.Prepare(ctx, sel.binding, a, identity); err != nil {
133 return "", err
134 }
135 }
136 return a.ID, nil
137}
138
139func (h *RootHistoryPass) CompleteAttempt(ctx context.Context, rt registry.Route, a HistoryAttempt) error {
140 sel, err := h.resolve(ctx, rt)
141 if err != nil {
142 return err
143 }
144 if sel.source != a.Source {
145 return os.ErrPermission
146 }
147 return h.complete(ctx, rt, sel, a.ID, a.Version)
148}
149
150func (h *RootHistoryPass) complete(ctx context.Context, rt registry.Route, sel historySelection, id, version string) error {
151 if h.Installed == nil {
152 return os.ErrPermission
153 }
154 if err := h.recheck(ctx, rt, sel); err != nil {
155 return err
156 }
157 program, err := h.Installed(ctx, rt, version)
158 if err != nil {
159 return err
160 }
161 if err := h.recheck(ctx, rt, sel); err != nil {
162 return err
163 }
164 r, err := h.History.read(sel.binding)
165 if err != nil {
166 return err
167 }
168 if r.Attempt == nil || r.Attempt.ID != id || r.Attempt.Version != version || r.Attempt.Source != sel.source || r.Revision == ^uint64(0) {
169 return os.ErrPermission
170 }
171 if program != "" && !slices.Contains(r.Programs.Values, program) {
172 r.Programs.Values = append(r.Programs.Values, program)
173 }
174 r.Revision++
175 r.Programs.Revision = r.Revision
176 r.Refusals.Values = slices.DeleteFunc(r.Refusals.Values, func(ref Refusal) bool { return ref.Version == version })
177 r.Refusals.Revision = r.Revision
178 r.Attempt = nil
179 return h.History.write(r)
180}
181
182func (c passConfig) historyError(err error) {
183 if err != nil && c.restartErrors != nil {
184 *c.restartErrors = append(*c.restartErrors, err)
185 }
186}
187
188func (c passConfig) recordRefusal(ctx context.Context, rt registry.Route, r Refusal) error {
189 if c.requireRootHistory {
190 sel, ok := c.historySelected[rt.Host]
191 if !ok {
192 return os.ErrPermission
193 }
194 if err := c.rootHistory.recheck(ctx, rt, sel); err != nil {
195 return err
196 }
197 if err := c.rootHistory.History.AddRefusal(sel.binding, r); err != nil {
198 return err
199 }
200 }
201 if c.bakeDir != "" {
202 return WriteRefusal(c.bakeDir, r)
203 }
204 return nil
205}
206