@@ -11,7 +11,7 @@ import * as util from "util";
11
11
import express from "express" ;
12
12
import { inject , injectable } from "inversify" ;
13
13
import { BearerAuth } from "../auth/bearer-authenticator" ;
14
- import { isWithFunctionAccessGuard } from "../auth/function-access" ;
14
+ import { WithFunctionAccessGuard } from "../auth/function-access" ;
15
15
import { CodeSyncResourceDB , ALL_SERVER_RESOURCES , ServerResource , SyncResource } from "@gitpod/gitpod-db/lib" ;
16
16
import {
17
17
DeleteRequest ,
@@ -26,6 +26,8 @@ import { v4 as uuidv4 } from "uuid";
26
26
import { accessCodeSyncStorage , UserRateLimiter } from "../auth/rate-limiter" ;
27
27
import { Config } from "../config" ;
28
28
import { CachingBlobServiceClientProvider } from "../util/content-service-sugar" ;
29
+ import { Authorizer } from "../authorization/authorizer" ;
30
+ import { FGAFunctionAccessGuard } from "../auth/resource-access" ;
29
31
30
32
// By default: 5 kind of resources * 20 revs * 1Mb = 100Mb max in the content service for user data.
31
33
const defaultRevLimit = 20 ;
@@ -71,17 +73,22 @@ function toObjectName(resource: ServerResource, rev: string, collection: string
71
73
72
74
@injectable ( )
73
75
export class CodeSyncService {
74
- @inject ( Config )
75
- private readonly config : Config ;
76
+ constructor (
77
+ @inject ( Config )
78
+ private readonly config : Config ,
76
79
77
- @inject ( BearerAuth )
78
- private readonly auth : BearerAuth ;
80
+ @inject ( BearerAuth )
81
+ private readonly auth : BearerAuth ,
79
82
80
- @inject ( CachingBlobServiceClientProvider )
81
- private readonly blobsProvider : CachingBlobServiceClientProvider ;
83
+ @inject ( CachingBlobServiceClientProvider )
84
+ private readonly blobsProvider : CachingBlobServiceClientProvider ,
82
85
83
- @inject ( CodeSyncResourceDB )
84
- private readonly db : CodeSyncResourceDB ;
86
+ @inject ( CodeSyncResourceDB )
87
+ private readonly db : CodeSyncResourceDB ,
88
+
89
+ @inject ( Authorizer )
90
+ private readonly authorizer : Authorizer ,
91
+ ) { }
85
92
86
93
get apiRouter ( ) : express . Router {
87
94
const config = this . config . codeSync ;
@@ -91,16 +98,16 @@ export class CodeSyncService {
91
98
res . setHeader ( "x-operation-id" , uuidv4 ( ) ) ;
92
99
return next ( ) ;
93
100
} ) ;
94
- router . use ( this . auth . restHandler ) ;
101
+ router . use ( this . auth . restHandler ) ; // expects Bearer token and authenticates req.user, throws otherwise
95
102
router . use ( async ( req , res , next ) => {
96
103
if ( ! User . is ( req . user ) ) {
97
104
res . sendStatus ( 400 ) ;
98
105
return ;
99
106
}
100
107
101
- const id = req . user . id ;
108
+ const userId = req . user . id ;
102
109
try {
103
- await UserRateLimiter . instance ( this . config . rateLimiter ) . consume ( id , accessCodeSyncStorage ) ;
110
+ await UserRateLimiter . instance ( this . config . rateLimiter ) . consume ( userId , accessCodeSyncStorage ) ;
104
111
} catch ( e ) {
105
112
if ( e instanceof Error ) {
106
113
throw e ;
@@ -110,20 +117,26 @@ export class CodeSyncService {
110
117
return ;
111
118
}
112
119
113
- if ( ! isWithFunctionAccessGuard ( req ) || ! req . functionGuard ?. canAccess ( accessCodeSyncStorage ) ) {
120
+ const functionGuard = ( req . user as WithFunctionAccessGuard ) . functionGuard ;
121
+ if ( ! ! functionGuard ) {
122
+ // If we authorize with scopes, there is a FunctionGuard
123
+ const guard = new FGAFunctionAccessGuard ( userId , functionGuard ) ;
124
+ if ( ! ( await guard . canAccess ( accessCodeSyncStorage ) ) ) {
125
+ res . sendStatus ( 403 ) ;
126
+ return ;
127
+ }
128
+ }
129
+
130
+ if ( ! ( await this . authorizer . hasPermissionOnUser ( userId , "code_sync" , userId ) ) ) {
114
131
res . sendStatus ( 403 ) ;
115
132
return ;
116
133
}
134
+
117
135
return next ( ) ;
118
136
} ) ;
119
137
120
138
router . get ( "/v1/manifest" , async ( req , res ) => {
121
- if ( ! User . is ( req . user ) ) {
122
- res . sendStatus ( 400 ) ;
123
- return ;
124
- }
125
-
126
- const manifest = await this . db . getManifest ( req . user . id ) ;
139
+ const manifest = await this . db . getManifest ( req . user ! . id ) ;
127
140
if ( ! manifest ) {
128
141
res . sendStatus ( 204 ) ;
129
142
return ;
@@ -149,13 +162,8 @@ export class CodeSyncService {
149
162
router . delete ( "/v1/collection/:collection/resource/:resource/:ref?" , this . deleteResource . bind ( this ) ) ;
150
163
router . delete ( "/v1/resource/:resource/:ref?" , this . deleteResource . bind ( this ) ) ;
151
164
router . delete ( "/v1/resource" , async ( req , res ) => {
152
- if ( ! User . is ( req . user ) ) {
153
- res . sendStatus ( 400 ) ;
154
- return ;
155
- }
156
-
157
165
// This endpoint is used to delete settings-sync data only
158
- const userId = req . user . id ;
166
+ const userId = req . user ! . id ;
159
167
await this . db . deleteSettingsSyncResources ( userId , async ( ) => {
160
168
const request = new DeleteRequest ( ) ;
161
169
request . setOwnerId ( userId ) ;
@@ -173,36 +181,21 @@ export class CodeSyncService {
173
181
} ) ;
174
182
175
183
router . get ( "/v1/collection" , async ( req , res ) => {
176
- if ( ! User . is ( req . user ) ) {
177
- res . sendStatus ( 400 ) ;
178
- return ;
179
- }
180
-
181
- const collections = await this . db . getCollections ( req . user . id ) ;
184
+ const collections = await this . db . getCollections ( req . user ! . id ) ;
182
185
183
186
res . json ( collections ) ;
184
187
return ;
185
188
} ) ;
186
189
router . post ( "/v1/collection" , async ( req , res ) => {
187
- if ( ! User . is ( req . user ) ) {
188
- res . sendStatus ( 400 ) ;
189
- return ;
190
- }
191
-
192
- const collection = await this . db . createCollection ( req . user . id ) ;
190
+ const collection = await this . db . createCollection ( req . user ! . id ) ;
193
191
194
192
res . type ( "text/plain" ) ;
195
193
res . send ( collection ) ;
196
194
return ;
197
195
} ) ;
198
196
router . delete ( "/v1/collection/:collection?" , async ( req , res ) => {
199
- if ( ! User . is ( req . user ) ) {
200
- res . sendStatus ( 400 ) ;
201
- return ;
202
- }
203
-
204
197
const { collection } = req . params ;
205
- await this . deleteCollection ( req . user . id , collection ) ;
198
+ await this . deleteCollection ( req . user ! . id , collection ) ;
206
199
207
200
res . sendStatus ( 200 ) ;
208
201
} ) ;
@@ -211,11 +204,6 @@ export class CodeSyncService {
211
204
}
212
205
213
206
private async getResources ( req : express . Request < { resource : string ; collection ?: string } > , res : express . Response ) {
214
- if ( ! User . is ( req . user ) ) {
215
- res . sendStatus ( 400 ) ;
216
- return ;
217
- }
218
-
219
207
const { resource, collection } = req . params ;
220
208
const resourceKey = ALL_SERVER_RESOURCES . find ( ( key ) => key === resource ) ;
221
209
if ( ! resourceKey ) {
@@ -224,14 +212,14 @@ export class CodeSyncService {
224
212
}
225
213
226
214
if ( collection ) {
227
- const valid = await this . db . isCollection ( req . user . id , collection ) ;
215
+ const valid = await this . db . isCollection ( req . user ! . id , collection ) ;
228
216
if ( ! valid ) {
229
217
res . sendStatus ( 405 ) ;
230
218
return ;
231
219
}
232
220
}
233
221
234
- const revs = await this . db . getResources ( req . user . id , resourceKey , collection ) ;
222
+ const revs = await this . db . getResources ( req . user ! . id , resourceKey , collection ) ;
235
223
if ( ! revs . length ) {
236
224
res . sendStatus ( 204 ) ;
237
225
return ;
@@ -249,11 +237,6 @@ export class CodeSyncService {
249
237
req : express . Request < { resource : string ; ref : string ; collection ?: string } > ,
250
238
res : express . Response ,
251
239
) {
252
- if ( ! User . is ( req . user ) ) {
253
- res . sendStatus ( 400 ) ;
254
- return ;
255
- }
256
-
257
240
const { resource, ref, collection } = req . params ;
258
241
const resourceKey = ALL_SERVER_RESOURCES . find ( ( key ) => key === resource ) ;
259
242
if ( ! resourceKey ) {
@@ -262,14 +245,14 @@ export class CodeSyncService {
262
245
}
263
246
264
247
if ( collection ) {
265
- const valid = await this . db . isCollection ( req . user . id , collection ) ;
248
+ const valid = await this . db . isCollection ( req . user ! . id , collection ) ;
266
249
if ( ! valid ) {
267
250
res . sendStatus ( 405 ) ;
268
251
return ;
269
252
}
270
253
}
271
254
272
- const resourceRev = ( await this . db . getResource ( req . user . id , resourceKey , ref , collection ) ) ?. rev ;
255
+ const resourceRev = ( await this . db . getResource ( req . user ! . id , resourceKey , ref , collection ) ) ?. rev ;
273
256
if ( ! resourceRev ) {
274
257
res . setHeader ( "etag" , "0" ) ;
275
258
res . sendStatus ( 204 ) ;
@@ -283,7 +266,7 @@ export class CodeSyncService {
283
266
let content : string ;
284
267
const contentType = req . headers [ "content-type" ] || "*/*" ;
285
268
const request = new DownloadUrlRequest ( ) ;
286
- request . setOwnerId ( req . user . id ) ;
269
+ request . setOwnerId ( req . user ! . id ) ;
287
270
request . setName ( toObjectName ( resourceKey , resourceRev , collection ) ) ;
288
271
request . setContentType ( contentType ) ;
289
272
try {
@@ -317,11 +300,6 @@ export class CodeSyncService {
317
300
}
318
301
319
302
private async postResource ( req : express . Request < { resource : string ; collection ?: string } > , res : express . Response ) {
320
- if ( ! User . is ( req . user ) ) {
321
- res . sendStatus ( 400 ) ;
322
- return ;
323
- }
324
-
325
303
const { resource, collection } = req . params ;
326
304
const resourceKey = ALL_SERVER_RESOURCES . find ( ( key ) => key === resource ) ;
327
305
if ( ! resourceKey ) {
@@ -330,7 +308,7 @@ export class CodeSyncService {
330
308
}
331
309
332
310
if ( collection ) {
333
- const valid = await this . db . isCollection ( req . user . id , collection ) ;
311
+ const valid = await this . db . isCollection ( req . user ! . id , collection ) ;
334
312
if ( ! valid ) {
335
313
res . sendStatus ( 405 ) ;
336
314
return ;
@@ -346,7 +324,7 @@ export class CodeSyncService {
346
324
this . config . codeSync ?. revLimit ||
347
325
defaultRevLimit ;
348
326
const isEditSessionsResource = resourceKey === "editSessions" ;
349
- const userId = req . user . id ;
327
+ const userId = req . user ! . id ;
350
328
const contentType = req . headers [ "content-type" ] || "*/*" ;
351
329
const newRev = await this . db . insert (
352
330
userId ,
@@ -403,11 +381,6 @@ export class CodeSyncService {
403
381
req : express . Request < { resource : string ; ref ?: string ; collection ?: string } > ,
404
382
res : express . Response ,
405
383
) {
406
- if ( ! User . is ( req . user ) ) {
407
- res . sendStatus ( 400 ) ;
408
- return ;
409
- }
410
-
411
384
// This endpoint is used to delete edit sessions data for now
412
385
413
386
const { resource, ref, collection } = req . params ;
@@ -418,14 +391,14 @@ export class CodeSyncService {
418
391
}
419
392
420
393
if ( collection ) {
421
- const valid = await this . db . isCollection ( req . user . id , collection ) ;
394
+ const valid = await this . db . isCollection ( req . user ! . id , collection ) ;
422
395
if ( ! valid ) {
423
396
res . sendStatus ( 405 ) ;
424
397
return ;
425
398
}
426
399
}
427
400
428
- await this . doDeleteResource ( req . user . id , resourceKey , ref , collection ) ;
401
+ await this . doDeleteResource ( req . user ! . id , resourceKey , ref , collection ) ;
429
402
res . sendStatus ( 200 ) ;
430
403
return ;
431
404
}
0 commit comments