carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "time"
6
7 "github.com/carlosframework/platform/internal/release"
8 "github.com/carlosframework/platform/internal/telemetry"
9)
10
11// The helpers in this file are the ordinary route writers' mutation guard.
12// Each is called after the handler's own authentication, authorization and
13// validation, and wraps exactly the write that follows them. With HostClaim
14// off the raw deployment bucket implements no release.AppMutationRunner and
15// the SQL store has no gate, so every helper runs its callback unchanged.
16
17// appMutation runs one validated app write as an app mutation of acct/app on
18// the installed deployment boundary. The callback must use the context it is
19// handed: that context carries the permit every bucket, SQL and backend
20// write below it is checked against. A caller that already holds a permitted
21// context for the same app joins it rather than opening a second operation.
22func (s *Server) appMutation(ctx context.Context, acct *Account, app, action string, fn func(context.Context) error) error {
23 return release.WithAppMutation(ctx, s.cfg.Releases, acct.Sqid, app, action, fn)
24}
25
26// updateAppTelemetry is telemetry.Update of the app's own settings object as
27// one app mutation. The account-wide default (telemetry.AccountKey) names no
28// app and stays a plain write.
29func (s *Server) updateAppTelemetry(ctx context.Context, acct *Account, app, action string, fn func(*telemetry.Settings)) error {
30 return s.appMutation(ctx, acct, app, action, func(ctx context.Context) error {
31 return telemetry.Update(ctx, s.cfg.Releases, telemetry.AppKey(acct.Sqid, app), fn)
32 })
33}
34
35// ConfirmChangeContext is ConfirmChange as a write of the change's own app.
36// The caller has already proved the change belongs to accountID/app
37// (pendingScoped); the armed gate refuses the write if it does not.
38func (s *Store) ConfirmChangeContext(ctx context.Context, accountID int64, app, id, address string, now time.Time) (pc *PendingChange, met bool, err error) {
39 err = s.withAutoAppSQL(ctx, accountID, app, "sql.ConfirmChange", func(v *Store) error {
40 pc, met, err = v.ConfirmChange(id, address, now)
41 return err
42 })
43 return pc, met, err
44}
45
46// MarkExecutedContext is MarkExecuted as a write of the change's own app.
47func (s *Store) MarkExecutedContext(ctx context.Context, accountID int64, app, id string, now time.Time) error {
48 return s.withAutoAppSQL(ctx, accountID, app, "sql.MarkExecuted", func(v *Store) error {
49 return v.MarkExecuted(id, now)
50 })
51}
52
53// DeleteChangeContext is DeleteChange as a write of the change's own app.
54func (s *Store) DeleteChangeContext(ctx context.Context, accountID int64, app, id string) error {
55 return s.withAutoAppSQL(ctx, accountID, app, "sql.DeleteChange", func(v *Store) error {
56 return v.DeleteChange(id)
57 })
58}
59
60// serviceCredentialWrite runs one rotate or revoke of the account's service
61// credential h. A credential scoped to an app is a row of that app and is
62// written as its app mutation; an account-wide credential (empty app) names
63// no app and is written directly, as the gate allows for cli_tokens rows with
64// an empty app column.
65// The scope is read from the credential before the write. A stale or failed
66// read can only pick the wrong scope, and the armed gate then refuses the
67// write rather than letting it land. Ungated, fn runs on the store as before
68// and nothing extra is read.
69func (s *Server) serviceCredentialWrite(ctx context.Context, acct *Account, h serviceHandle, action string, fn func(*Store) error) error {
70 if s.store.gate == nil {
71 return fn(s.store)
72 }
73 rows, err := s.store.ServicePrincipals(acct.ID)
74 if err != nil {
75 return err
76 }
77 for _, row := range rows {
78 if (h.name != "" && row.Name == h.name) || (h.id != "" && row.ID == h.id) {
79 return s.store.withAutoAppSQL(ctx, acct.ID, row.App, action, fn)
80 }
81 }
82 return fn(s.store)
83}
84