@@ -303,4 +303,89 @@ describe('middleware', function() {
303
303
} ) ;
304
304
} ) ;
305
305
} ) ;
306
+
307
+ describe ( 'access control' , function ( ) {
308
+ function setupOpMiddleware ( backend ) {
309
+ backend . use ( 'apply' , function ( request , next ) {
310
+ request . priorAccountId = request . snapshot . data && request . snapshot . data . accountId ;
311
+ next ( ) ;
312
+ } ) ;
313
+ backend . use ( 'commit' , function ( request , next ) {
314
+ var accountId = ( request . snapshot . data ) ?
315
+ // For created documents, get the accountId from the document data
316
+ request . snapshot . data . accountId :
317
+ // For deleted documents, get the accountId from before
318
+ request . priorAccountId ;
319
+ // Store the accountId for the document on the op for efficient access control
320
+ request . op . accountId = accountId ;
321
+ next ( ) ;
322
+ } ) ;
323
+ backend . use ( 'op' , function ( request , next ) {
324
+ if ( request . op . accountId === request . agent . accountId ) {
325
+ return next ( ) ;
326
+ }
327
+ var err = { message : 'op accountId does not match' , code : 'ERR_OP_READ_FORBIDDEN' } ;
328
+ return next ( err ) ;
329
+ } ) ;
330
+ }
331
+
332
+ it ( 'is possible to cache add additional top-level fields on ops for access control' , function ( done ) {
333
+ setupOpMiddleware ( this . backend ) ;
334
+ var connection1 = this . backend . connect ( ) ;
335
+ var connection2 = this . backend . connect ( ) ;
336
+ connection2 . agent . accountId = 'foo' ;
337
+
338
+ // Fetching the snapshot here will cause subsequent fetches to get ops
339
+ connection2 . get ( 'dogs' , 'fido' ) . fetch ( function ( err ) {
340
+ if ( err ) return done ( err ) ;
341
+ var data = { accountId : 'foo' , age : 2 } ;
342
+ connection1 . get ( 'dogs' , 'fido' ) . create ( data , function ( err ) {
343
+ if ( err ) return done ( err ) ;
344
+ // This will go through the 'op' middleware and should pass
345
+ connection2 . get ( 'dogs' , 'fido' ) . fetch ( done ) ;
346
+ } ) ;
347
+ } ) ;
348
+ } ) ;
349
+
350
+ it ( 'op middleware can reject ops' , function ( done ) {
351
+ setupOpMiddleware ( this . backend ) ;
352
+ var connection1 = this . backend . connect ( ) ;
353
+ var connection2 = this . backend . connect ( ) ;
354
+ connection2 . agent . accountId = 'baz' ;
355
+
356
+ // Fetching the snapshot here will cause subsequent fetches to get ops
357
+ connection2 . get ( 'dogs' , 'fido' ) . fetch ( function ( err ) {
358
+ if ( err ) return done ( err ) ;
359
+ var data = { accountId : 'foo' , age : 2 } ;
360
+ connection1 . get ( 'dogs' , 'fido' ) . create ( data , function ( err ) {
361
+ if ( err ) return done ( err ) ;
362
+ // This will go through the 'op' middleware and fail;
363
+ connection2 . get ( 'dogs' , 'fido' ) . fetch ( function ( err ) {
364
+ expect ( err . code ) . equal ( 'ERR_OP_READ_FORBIDDEN' ) ;
365
+ done ( ) ;
366
+ } ) ;
367
+ } ) ;
368
+ } ) ;
369
+ } ) ;
370
+
371
+ it ( 'pubsub subscribe can check top-level fields for access control' , function ( done ) {
372
+ setupOpMiddleware ( this . backend ) ;
373
+ var connection1 = this . backend . connect ( ) ;
374
+ var connection2 = this . backend . connect ( ) ;
375
+ connection2 . agent . accountId = 'foo' ;
376
+
377
+ // Fetching the snapshot here will cause subsequent fetches to get ops
378
+ connection2 . get ( 'dogs' , 'fido' ) . subscribe ( function ( err ) {
379
+ if ( err ) return done ( err ) ;
380
+ var data = { accountId : 'foo' , age : 2 } ;
381
+ connection1 . get ( 'dogs' , 'fido' ) . create ( data , function ( err ) {
382
+ if ( err ) return done ( err ) ;
383
+ // The subscribed op will go through the 'op' middleware and should pass
384
+ connection2 . get ( 'dogs' , 'fido' ) . on ( 'create' , function ( ) {
385
+ done ( ) ;
386
+ } ) ;
387
+ } ) ;
388
+ } ) ;
389
+ } ) ;
390
+ } ) ;
306
391
} ) ;
0 commit comments