@@ -812,6 +812,153 @@ describe('rest create', () => {
812812 expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "Clients aren't allowed to perform the get operation on the _GlobalConfig collection." ) ) ;
813813 } ) ;
814814
815+ it ( 'should require master key for all volatile classes' , ( ) => {
816+ // This test guards against drift between volatileClasses (SchemaController.js)
817+ // and classesWithMasterOnlyAccess (SharedRest.js). If a new volatile class is
818+ // added, it must also be added to classesWithMasterOnlyAccess and this test.
819+ const volatileClasses = [
820+ '_JobStatus' ,
821+ '_PushStatus' ,
822+ '_Hooks' ,
823+ '_GlobalConfig' ,
824+ '_GraphQLConfig' ,
825+ '_JobSchedule' ,
826+ '_Audience' ,
827+ '_Idempotency' ,
828+ ] ;
829+ for ( const className of volatileClasses ) {
830+ expect ( ( ) =>
831+ rest . create ( config , auth . nobody ( config ) , className , { } )
832+ ) . toThrowMatching (
833+ e => e . code === Parse . Error . OPERATION_FORBIDDEN ,
834+ `Expected ${ className } to require master key`
835+ ) ;
836+ }
837+ } ) ;
838+
839+ it ( 'cannot find objects in _GraphQLConfig without masterKey' , async ( ) => {
840+ await config . parseGraphQLController . updateGraphQLConfig ( { enabledForClasses : [ '_User' ] } ) ;
841+ await expectAsync (
842+ rest . find ( config , auth . nobody ( config ) , '_GraphQLConfig' , { } )
843+ ) . toBeRejectedWith (
844+ jasmine . objectContaining ( { code : Parse . Error . OPERATION_FORBIDDEN } )
845+ ) ;
846+ } ) ;
847+
848+ it ( 'cannot update object in _GraphQLConfig without masterKey' , async ( ) => {
849+ await config . parseGraphQLController . updateGraphQLConfig ( { enabledForClasses : [ '_User' ] } ) ;
850+ expect ( ( ) =>
851+ rest . update ( config , auth . nobody ( config ) , '_GraphQLConfig' , '1' , {
852+ config : { enabledForClasses : [ ] } ,
853+ } )
854+ ) . toThrowMatching ( e => e . code === Parse . Error . OPERATION_FORBIDDEN ) ;
855+ } ) ;
856+
857+ it ( 'cannot delete object in _GraphQLConfig without masterKey' , async ( ) => {
858+ await config . parseGraphQLController . updateGraphQLConfig ( { enabledForClasses : [ '_User' ] } ) ;
859+ expect ( ( ) =>
860+ rest . del ( config , auth . nobody ( config ) , '_GraphQLConfig' , '1' )
861+ ) . toThrowMatching ( e => e . code === Parse . Error . OPERATION_FORBIDDEN ) ;
862+ } ) ;
863+
864+ it ( 'can perform operations on _GraphQLConfig with masterKey' , async ( ) => {
865+ await config . parseGraphQLController . updateGraphQLConfig ( { enabledForClasses : [ '_User' ] } ) ;
866+ const found = await rest . find ( config , auth . master ( config ) , '_GraphQLConfig' , { } ) ;
867+ expect ( found . results . length ) . toBeGreaterThan ( 0 ) ;
868+ await rest . del ( config , auth . master ( config ) , '_GraphQLConfig' , '1' ) ;
869+ const afterDelete = await rest . find ( config , auth . master ( config ) , '_GraphQLConfig' , { } ) ;
870+ expect ( afterDelete . results . length ) . toBe ( 0 ) ;
871+ } ) ;
872+
873+ it ( 'cannot create object in _Audience without masterKey' , ( ) => {
874+ expect ( ( ) =>
875+ rest . create ( config , auth . nobody ( config ) , '_Audience' , {
876+ name : 'test' ,
877+ query : '{}' ,
878+ } )
879+ ) . toThrowMatching ( e => e . code === Parse . Error . OPERATION_FORBIDDEN ) ;
880+ } ) ;
881+
882+ it ( 'cannot find objects in _Audience without masterKey' , async ( ) => {
883+ await expectAsync (
884+ rest . find ( config , auth . nobody ( config ) , '_Audience' , { } )
885+ ) . toBeRejectedWith (
886+ jasmine . objectContaining ( { code : Parse . Error . OPERATION_FORBIDDEN } )
887+ ) ;
888+ } ) ;
889+
890+ it ( 'cannot update object in _Audience without masterKey' , async ( ) => {
891+ const obj = await rest . create ( config , auth . master ( config ) , '_Audience' , {
892+ name : 'test' ,
893+ query : '{}' ,
894+ } ) ;
895+ expect ( ( ) =>
896+ rest . update ( config , auth . nobody ( config ) , '_Audience' , obj . response . objectId , {
897+ name : 'updated' ,
898+ } )
899+ ) . toThrowMatching ( e => e . code === Parse . Error . OPERATION_FORBIDDEN ) ;
900+ } ) ;
901+
902+ it ( 'cannot delete object in _Audience without masterKey' , async ( ) => {
903+ const obj = await rest . create ( config , auth . master ( config ) , '_Audience' , {
904+ name : 'test' ,
905+ query : '{}' ,
906+ } ) ;
907+ expect ( ( ) =>
908+ rest . del ( config , auth . nobody ( config ) , '_Audience' , obj . response . objectId )
909+ ) . toThrowMatching ( e => e . code === Parse . Error . OPERATION_FORBIDDEN ) ;
910+ } ) ;
911+
912+ it ( 'can perform CRUD on _Audience with masterKey' , async ( ) => {
913+ const obj = await rest . create ( config , auth . master ( config ) , '_Audience' , {
914+ name : 'test' ,
915+ query : '{}' ,
916+ } ) ;
917+ expect ( obj . response . objectId ) . toBeDefined ( ) ;
918+ const found = await rest . find ( config , auth . master ( config ) , '_Audience' , { } ) ;
919+ expect ( found . results . length ) . toBeGreaterThan ( 0 ) ;
920+ await rest . del ( config , auth . master ( config ) , '_Audience' , obj . response . objectId ) ;
921+ const afterDelete = await rest . find ( config , auth . master ( config ) , '_Audience' , { } ) ;
922+ expect ( afterDelete . results . length ) . toBe ( 0 ) ;
923+ } ) ;
924+
925+ it ( 'cannot access _GraphQLConfig via class route without masterKey' , async ( ) => {
926+ await config . parseGraphQLController . updateGraphQLConfig ( { enabledForClasses : [ '_User' ] } ) ;
927+ try {
928+ await request ( {
929+ url : 'http://localhost:8378/1/classes/_GraphQLConfig' ,
930+ json : true ,
931+ headers : {
932+ 'X-Parse-Application-Id' : 'test' ,
933+ 'X-Parse-REST-API-Key' : 'rest' ,
934+ } ,
935+ } ) ;
936+ fail ( 'should have thrown' ) ;
937+ } catch ( e ) {
938+ expect ( e . data . code ) . toBe ( Parse . Error . OPERATION_FORBIDDEN ) ;
939+ }
940+ } ) ;
941+
942+ it ( 'cannot access _Audience via class route without masterKey' , async ( ) => {
943+ await rest . create ( config , auth . master ( config ) , '_Audience' , {
944+ name : 'test' ,
945+ query : '{}' ,
946+ } ) ;
947+ try {
948+ await request ( {
949+ url : 'http://localhost:8378/1/classes/_Audience' ,
950+ json : true ,
951+ headers : {
952+ 'X-Parse-Application-Id' : 'test' ,
953+ 'X-Parse-REST-API-Key' : 'rest' ,
954+ } ,
955+ } ) ;
956+ fail ( 'should have thrown' ) ;
957+ } catch ( e ) {
958+ expect ( e . data . code ) . toBe ( Parse . Error . OPERATION_FORBIDDEN ) ;
959+ }
960+ } ) ;
961+
815962 it ( 'locks down session' , done => {
816963 let currentUser ;
817964 Parse . User . signUp ( 'foo' , 'bar' )
0 commit comments