carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package objstore
2
3import (
4 "context"
5 "fmt"
6 "sync"
7)
8
9// MemUser is the in-memory fake's view of an IAM principal: its ARN and its
10// live access keys (id -> secret). Exported so tests in this package can
11// assert on p.Users["u1"].Keys directly.
12type MemUser struct {
13 ARN string
14 Keys map[string]string
15 // Policy is the inline policy document EnsureUser last wrote. The real
16 // provisioner's PutUserPolicy overwrites wholesale on every call, so the
17 // fake records it on every call too — including for a user that already
18 // exists, which is the only case enforcement is ever turned on for.
19 Policy string
20}
21
22// MemProvisioner is an in-memory Provisioner for walk tests: no AWS, no
23// network, deterministic key ids. It stays in this package (internal) —
24// nothing outside objstore needs to reach into its state.
25type MemProvisioner struct {
26 mu sync.Mutex
27 region string
28 nextKey int
29
30 // Buckets maps bucket name -> object key -> body.
31 Buckets map[string]map[string][]byte
32 // Users maps user name -> MemUser.
33 Users map[string]MemUser
34 // FailNext, keyed by method name, is consumed one-shot: the next call
35 // to that method fails with the given error, then the map entry is
36 // gone and subsequent calls succeed normally.
37 FailNext map[string]error
38 // MintSecret, when non-empty, is the secret every MintKey hands back
39 // instead of a generated one — the seam for testing what a walk does
40 // with a secret it cannot deliver (a real backend picks the bytes, so
41 // a walk must not assume they are renderable).
42 MintSecret string
43}
44
45// NewMemProvisioner returns an empty fake that answers region for every
46// EnsureBucket call.
47func NewMemProvisioner(region string) *MemProvisioner {
48 return &MemProvisioner{
49 region: region,
50 Buckets: map[string]map[string][]byte{},
51 Users: map[string]MemUser{},
52 FailNext: map[string]error{},
53 }
54}
55
56// takeFailure consumes and returns FailNext[method], if any, under the
57// caller's lock. Consumption happens before any state change so a failed
58// call leaves state untouched.
59func (p *MemProvisioner) takeFailure(method string) error {
60 if err, ok := p.FailNext[method]; ok {
61 delete(p.FailNext, method)
62 return err
63 }
64 return nil
65}
66
67func (p *MemProvisioner) EnsureBucket(ctx context.Context, name string) (string, error) {
68 p.mu.Lock()
69 defer p.mu.Unlock()
70 if err := p.takeFailure("EnsureBucket"); err != nil {
71 return "", err
72 }
73 if p.Buckets[name] == nil {
74 p.Buckets[name] = map[string][]byte{}
75 }
76 return p.region, nil
77}
78
79func (p *MemProvisioner) EnsureUser(ctx context.Context, name, path, bucketARN string, deny *DenyTag) (string, error) {
80 p.mu.Lock()
81 defer p.mu.Unlock()
82 if err := p.takeFailure("EnsureUser"); err != nil {
83 return "", err
84 }
85 policy := storePolicyJSON(bucketARN, deny)
86 if u, ok := p.Users[name]; ok {
87 // The policy is rewritten for an existing principal too, mirroring
88 // PutUserPolicy running unconditionally on both of awsProvisioner's
89 // branches. A fake that recorded it only on the create branch would
90 // make enforcement look broken exactly where it matters: every store
91 // this is ever turned on for already has its user.
92 u.Policy = policy
93 p.Users[name] = u // MemUser is a value in the map — mutate, store back
94 return u.ARN, nil
95 }
96 arn := "arn:mem:iam::user" + path + name
97 p.Users[name] = MemUser{ARN: arn, Keys: map[string]string{}, Policy: policy}
98 return arn, nil
99}
100
101// PolicyFor is the inline policy the fake last wrote for a user, so a walk
102// test can assert that enforcement reached the backend rather than only
103// reaching the record.
104func (p *MemProvisioner) PolicyFor(user string) string {
105 p.mu.Lock()
106 defer p.mu.Unlock()
107 return p.Users[user].Policy
108}
109
110func (p *MemProvisioner) MintKey(ctx context.Context, user string) (string, string, error) {
111 p.mu.Lock()
112 defer p.mu.Unlock()
113 if err := p.takeFailure("MintKey"); err != nil {
114 return "", "", err
115 }
116 u, ok := p.Users[user]
117 if !ok {
118 return "", "", fmt.Errorf("objstore: mem: no such user %q", user)
119 }
120 p.nextKey++
121 id := fmt.Sprintf("MEMKEY%d", p.nextKey)
122 secret := fmt.Sprintf("MEMSEC%d", p.nextKey)
123 if p.MintSecret != "" {
124 secret = p.MintSecret
125 }
126 u.Keys[id] = secret
127 return id, secret, nil
128}
129
130func (p *MemProvisioner) DeleteKey(ctx context.Context, user, id string) error {
131 p.mu.Lock()
132 defer p.mu.Unlock()
133 if err := p.takeFailure("DeleteKey"); err != nil {
134 return err
135 }
136 if u, ok := p.Users[user]; ok {
137 delete(u.Keys, id)
138 }
139 return nil
140}
141
142func (p *MemProvisioner) ReapKeys(ctx context.Context, user string, keep []string) error {
143 p.mu.Lock()
144 defer p.mu.Unlock()
145 if err := p.takeFailure("ReapKeys"); err != nil {
146 return err
147 }
148 u, ok := p.Users[user]
149 if !ok {
150 return nil
151 }
152 keepSet := make(map[string]bool, len(keep))
153 for _, id := range keep {
154 keepSet[id] = true
155 }
156 for id := range u.Keys {
157 if !keepSet[id] {
158 delete(u.Keys, id)
159 }
160 }
161 return nil
162}
163
164func (p *MemProvisioner) EmptyBucket(ctx context.Context, name string) error {
165 p.mu.Lock()
166 defer p.mu.Unlock()
167 if err := p.takeFailure("EmptyBucket"); err != nil {
168 return err
169 }
170 if b, ok := p.Buckets[name]; ok {
171 for k := range b {
172 delete(b, k)
173 }
174 }
175 return nil
176}
177
178func (p *MemProvisioner) DeleteBucket(ctx context.Context, name string) error {
179 p.mu.Lock()
180 defer p.mu.Unlock()
181 if err := p.takeFailure("DeleteBucket"); err != nil {
182 return err
183 }
184 delete(p.Buckets, name)
185 return nil
186}
187
188func (p *MemProvisioner) DeleteUser(ctx context.Context, user string) error {
189 p.mu.Lock()
190 defer p.mu.Unlock()
191 if err := p.takeFailure("DeleteUser"); err != nil {
192 return err
193 }
194 delete(p.Users, user)
195 return nil
196}
197