@@ -13,33 +13,41 @@ import (
13
13
14
14
archivefs "github.com/coder/coder/v2/archive/fs"
15
15
"github.com/coder/coder/v2/coderd/database"
16
+ "github.com/coder/coder/v2/coderd/database/dbauthz"
17
+ "github.com/coder/coder/v2/coderd/rbac"
18
+ "github.com/coder/coder/v2/coderd/rbac/policy"
16
19
"github.com/coder/coder/v2/coderd/util/lazy"
17
20
)
18
21
19
22
// NewFromStore returns a file cache that will fetch files from the provided
20
23
// database.
21
- func NewFromStore (store database.Store , registerer prometheus.Registerer ) * Cache {
22
- fetch := func (ctx context.Context , fileID uuid.UUID ) (cacheEntryValue , error ) {
23
- file , err := store .GetFileByID (ctx , fileID )
24
+ func NewFromStore (store database.Store , registerer prometheus.Registerer , authz rbac.Authorizer ) * Cache {
25
+ fetch := func (ctx context.Context , fileID uuid.UUID ) (CacheEntryValue , error ) {
26
+ // Make sure the read does not fail due to authorization issues.
27
+ // Authz is checked on the Acquire call, so this is safe.
28
+ //nolint:gocritic
29
+ file , err := store .GetFileByID (dbauthz .AsFileReader (ctx ), fileID )
24
30
if err != nil {
25
- return cacheEntryValue {}, xerrors .Errorf ("failed to read file from database: %w" , err )
31
+ return CacheEntryValue {}, xerrors .Errorf ("failed to read file from database: %w" , err )
26
32
}
27
33
28
34
content := bytes .NewBuffer (file .Data )
29
- return cacheEntryValue {
30
- FS : archivefs .FromTarReader (content ),
31
- size : int64 (content .Len ()),
35
+ return CacheEntryValue {
36
+ Object : file .RBACObject (),
37
+ FS : archivefs .FromTarReader (content ),
38
+ Size : int64 (content .Len ()),
32
39
}, nil
33
40
}
34
41
35
- return New (fetch , registerer )
42
+ return New (fetch , registerer , authz )
36
43
}
37
44
38
- func New (fetch fetcher , registerer prometheus.Registerer ) * Cache {
45
+ func New (fetch fetcher , registerer prometheus.Registerer , authz rbac. Authorizer ) * Cache {
39
46
return (& Cache {
40
47
lock : sync.Mutex {},
41
48
data : make (map [uuid.UUID ]* cacheEntry ),
42
49
fetcher : fetch ,
50
+ authz : authz ,
43
51
}).registerMetrics (registerer )
44
52
}
45
53
@@ -101,6 +109,7 @@ type Cache struct {
101
109
lock sync.Mutex
102
110
data map [uuid.UUID ]* cacheEntry
103
111
fetcher
112
+ authz rbac.Authorizer
104
113
105
114
// metrics
106
115
cacheMetrics
@@ -117,18 +126,19 @@ type cacheMetrics struct {
117
126
totalCacheSize prometheus.Counter
118
127
}
119
128
120
- type cacheEntryValue struct {
129
+ type CacheEntryValue struct {
121
130
fs.FS
122
- size int64
131
+ Object rbac.Object
132
+ Size int64
123
133
}
124
134
125
135
type cacheEntry struct {
126
136
// refCount must only be accessed while the Cache lock is held.
127
137
refCount int
128
- value * lazy.ValueWithError [cacheEntryValue ]
138
+ value * lazy.ValueWithError [CacheEntryValue ]
129
139
}
130
140
131
- type fetcher func (context.Context , uuid.UUID ) (cacheEntryValue , error )
141
+ type fetcher func (context.Context , uuid.UUID ) (CacheEntryValue , error )
132
142
133
143
// Acquire will load the fs.FS for the given file. It guarantees that parallel
134
144
// calls for the same fileID will only result in one fetch, and that parallel
@@ -146,22 +156,33 @@ func (c *Cache) Acquire(ctx context.Context, fileID uuid.UUID) (fs.FS, error) {
146
156
c .Release (fileID )
147
157
return nil , err
148
158
}
159
+
160
+ subject , ok := dbauthz .ActorFromContext (ctx )
161
+ if ! ok {
162
+ return nil , dbauthz .ErrNoActor
163
+ }
164
+ // Always check the caller can actually read the file.
165
+ if err := c .authz .Authorize (ctx , subject , policy .ActionRead , it .Object ); err != nil {
166
+ c .Release (fileID )
167
+ return nil , err
168
+ }
169
+
149
170
return it .FS , err
150
171
}
151
172
152
- func (c * Cache ) prepare (ctx context.Context , fileID uuid.UUID ) * lazy.ValueWithError [cacheEntryValue ] {
173
+ func (c * Cache ) prepare (ctx context.Context , fileID uuid.UUID ) * lazy.ValueWithError [CacheEntryValue ] {
153
174
c .lock .Lock ()
154
175
defer c .lock .Unlock ()
155
176
156
177
entry , ok := c .data [fileID ]
157
178
if ! ok {
158
- value := lazy .NewWithError (func () (cacheEntryValue , error ) {
179
+ value := lazy .NewWithError (func () (CacheEntryValue , error ) {
159
180
val , err := c .fetcher (ctx , fileID )
160
181
161
182
// Always add to the cache size the bytes of the file loaded.
162
183
if err == nil {
163
- c .currentCacheSize .Add (float64 (val .size ))
164
- c .totalCacheSize .Add (float64 (val .size ))
184
+ c .currentCacheSize .Add (float64 (val .Size ))
185
+ c .totalCacheSize .Add (float64 (val .Size ))
165
186
}
166
187
167
188
return val , err
@@ -206,7 +227,7 @@ func (c *Cache) Release(fileID uuid.UUID) {
206
227
207
228
ev , err := entry .value .Load ()
208
229
if err == nil {
209
- c .currentCacheSize .Add (- 1 * float64 (ev .size ))
230
+ c .currentCacheSize .Add (- 1 * float64 (ev .Size ))
210
231
}
211
232
212
233
delete (c .data , fileID )
0 commit comments