2018-09-01 13:58:06 -04:00
'use strict' ;
2016-02-22 16:45:41 +01:00
// These tests check the "create" / "update" functionality of the REST API.
2018-07-02 23:30:14 -04:00
const auth = require ( '../lib/Auth' ) ;
const Config = require ( '../lib/Config' ) ;
2018-02-17 09:55:30 -05:00
const Parse = require ( 'parse/node' ) . Parse ;
2018-07-02 23:30:14 -04:00
const rest = require ( '../lib/rest' ) ;
const RestWrite = require ( '../lib/RestWrite' ) ;
2018-09-24 17:07:51 -04:00
const request = require ( '../lib/request' ) ;
2016-01-28 10:58:12 -08:00
2017-05-14 10:50:53 -04:00
let config ;
let database ;
2016-01-28 10:58:12 -08:00
describe ( 'rest create' , ( ) => {
2025-11-23 13:51:42 +01:00
let loggerErrorSpy ;
2016-11-24 15:47:41 -05:00
beforeEach ( ( ) => {
2017-10-23 08:43:05 -04:00
config = Config . get ( 'test' ) ;
2017-05-14 10:50:53 -04:00
database = config . database ;
2025-11-23 13:51:42 +01:00
const logger = require ( '../lib/logger' ) . default ;
loggerErrorSpy = spyOn ( logger , 'error' ) . and . callThrough ( ) ;
2016-08-20 16:07:48 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'handles _id' , done => {
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , 'Foo' , { } )
2017-06-20 09:15:26 -07:00
. then ( ( ) => database . adapter . find ( 'Foo' , { fields : { } } , { } , { } ) )
2020-10-25 15:06:58 -05:00
. then ( results => {
2017-06-20 09:15:26 -07:00
expect ( results . length ) . toEqual ( 1 ) ;
2018-02-17 09:55:30 -05:00
const obj = results [ 0 ] ;
2017-06-20 09:15:26 -07:00
expect ( typeof obj . objectId ) . toEqual ( 'string' ) ;
2017-06-27 11:22:43 +01:00
expect ( obj . objectId . length ) . toEqual ( 10 ) ;
2017-06-20 09:15:26 -07:00
expect ( obj . _id ) . toBeUndefined ( ) ;
done ( ) ;
} ) ;
2016-01-28 10:58:12 -08:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'can use custom _id size' , done => {
2017-06-27 11:22:43 +01:00
config . objectIdSize = 20 ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , 'Foo' , { } )
2017-06-27 11:22:43 +01:00
. then ( ( ) => database . adapter . find ( 'Foo' , { fields : { } } , { } , { } ) )
2020-10-25 15:06:58 -05:00
. then ( results => {
2017-06-27 11:22:43 +01:00
expect ( results . length ) . toEqual ( 1 ) ;
2018-02-17 09:55:30 -05:00
const obj = results [ 0 ] ;
2017-06-27 11:22:43 +01:00
expect ( typeof obj . objectId ) . toEqual ( 'string' ) ;
expect ( obj . objectId . length ) . toEqual ( 20 ) ;
done ( ) ;
} ) ;
} ) ;
2019-12-17 19:23:18 +01:00
it ( 'should use objectId from client when allowCustomObjectId true' , async ( ) => {
config . allowCustomObjectId = true ;
// use time as unique custom id for test reusability
const customId = ` ${ Date . now ( ) } ` ;
const obj = {
objectId : customId ,
} ;
const {
status ,
response : { objectId } ,
} = await rest . create ( config , auth . nobody ( config ) , 'MyClass' , obj ) ;
expect ( status ) . toEqual ( 201 ) ;
expect ( objectId ) . toEqual ( customId ) ;
} ) ;
it ( 'should throw on invalid objectId when allowCustomObjectId true' , ( ) => {
config . allowCustomObjectId = true ;
const objIdNull = {
objectId : null ,
} ;
const objIdUndef = {
objectId : undefined ,
} ;
const objIdEmpty = {
objectId : '' ,
} ;
const err = 'objectId must not be empty, null or undefined' ;
2020-10-25 15:06:58 -05:00
expect ( ( ) => rest . create ( config , auth . nobody ( config ) , 'MyClass' , objIdEmpty ) ) . toThrowError ( err ) ;
2019-12-17 19:23:18 +01:00
2020-10-25 15:06:58 -05:00
expect ( ( ) => rest . create ( config , auth . nobody ( config ) , 'MyClass' , objIdNull ) ) . toThrowError ( err ) ;
2019-12-17 19:23:18 +01:00
2020-10-25 15:06:58 -05:00
expect ( ( ) => rest . create ( config , auth . nobody ( config ) , 'MyClass' , objIdUndef ) ) . toThrowError ( err ) ;
2019-12-17 19:23:18 +01:00
} ) ;
it ( 'should generate objectId when not set by client with allowCustomObjectId true' , async ( ) => {
config . allowCustomObjectId = true ;
const {
status ,
response : { objectId } ,
} = await rest . create ( config , auth . nobody ( config ) , 'MyClass' , { } ) ;
expect ( status ) . toEqual ( 201 ) ;
expect ( objectId ) . toBeDefined ( ) ;
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'is backwards compatible when _id size changes' , done => {
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , 'Foo' , { size : 10 } )
2017-06-27 11:22:43 +01:00
. then ( ( ) => {
config . objectIdSize = 20 ;
2018-09-01 13:58:06 -04:00
return rest . find ( config , auth . nobody ( config ) , 'Foo' , { size : 10 } ) ;
2017-06-27 11:22:43 +01:00
} )
2020-10-25 15:06:58 -05:00
. then ( response => {
2017-06-27 11:22:43 +01:00
expect ( response . results . length ) . toEqual ( 1 ) ;
expect ( response . results [ 0 ] . objectId . length ) . toEqual ( 10 ) ;
2018-09-01 13:58:06 -04:00
return rest . update (
config ,
auth . nobody ( config ) ,
'Foo' ,
{ objectId : response . results [ 0 ] . objectId } ,
{ update : 20 }
) ;
2017-06-27 11:22:43 +01:00
} )
. then ( ( ) => {
2018-09-01 13:58:06 -04:00
return rest . find ( config , auth . nobody ( config ) , 'Foo' , { size : 10 } ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( response => {
2017-06-27 11:22:43 +01:00
expect ( response . results . length ) . toEqual ( 1 ) ;
expect ( response . results [ 0 ] . objectId . length ) . toEqual ( 10 ) ;
expect ( response . results [ 0 ] . update ) . toEqual ( 20 ) ;
2018-09-01 13:58:06 -04:00
return rest . create ( config , auth . nobody ( config ) , 'Foo' , { size : 20 } ) ;
2017-06-27 11:22:43 +01:00
} )
. then ( ( ) => {
config . objectIdSize = 10 ;
2018-09-01 13:58:06 -04:00
return rest . find ( config , auth . nobody ( config ) , 'Foo' , { size : 20 } ) ;
2017-06-27 11:22:43 +01:00
} )
2020-10-25 15:06:58 -05:00
. then ( response => {
2017-06-27 11:22:43 +01:00
expect ( response . results . length ) . toEqual ( 1 ) ;
expect ( response . results [ 0 ] . objectId . length ) . toEqual ( 20 ) ;
done ( ) ;
} ) ;
} ) ;
2023-09-29 13:17:48 -07:00
describe ( 'with maintenance key' , ( ) => {
let req ;
async function getObject ( id ) {
const res = await request ( {
headers : {
'X-Parse-Application-Id' : 'test' ,
2023-12-10 02:42:40 +01:00
'X-Parse-REST-API-Key' : 'rest' ,
2023-09-29 13:17:48 -07:00
} ,
method : 'GET' ,
2023-12-10 02:42:40 +01:00
url : ` http://localhost:8378/1/classes/TestObject/ ${ id } ` ,
2023-09-29 13:17:48 -07:00
} ) ;
return res . data ;
}
beforeEach ( ( ) => {
req = {
headers : {
'Content-Type' : 'application/json' ,
'X-Parse-Application-Id' : 'test' ,
'X-Parse-REST-API-Key' : 'rest' ,
2023-12-10 02:42:40 +01:00
'X-Parse-Maintenance-Key' : 'testing' ,
2023-09-29 13:17:48 -07:00
} ,
method : 'POST' ,
2023-12-10 02:42:40 +01:00
url : 'http://localhost:8378/1/classes/TestObject' ,
2023-09-29 13:17:48 -07:00
} ;
} ) ;
it ( 'allows createdAt' , async ( ) => {
const createdAt = { _ _type : 'Date' , iso : '2019-01-01T00:00:00.000Z' } ;
req . body = { createdAt } ;
const res = await request ( req ) ;
expect ( res . data . createdAt ) . toEqual ( createdAt . iso ) ;
} ) ;
it ( 'allows createdAt and updatedAt' , async ( ) => {
const createdAt = { _ _type : 'Date' , iso : '2019-01-01T00:00:00.000Z' } ;
const updatedAt = { _ _type : 'Date' , iso : '2019-02-01T00:00:00.000Z' } ;
req . body = { createdAt , updatedAt } ;
const res = await request ( req ) ;
const obj = await getObject ( res . data . objectId ) ;
expect ( obj . createdAt ) . toEqual ( createdAt . iso ) ;
expect ( obj . updatedAt ) . toEqual ( updatedAt . iso ) ;
} ) ;
it ( 'allows createdAt, updatedAt, and additional field' , async ( ) => {
const createdAt = { _ _type : 'Date' , iso : '2019-01-01T00:00:00.000Z' } ;
const updatedAt = { _ _type : 'Date' , iso : '2019-02-01T00:00:00.000Z' } ;
req . body = { createdAt , updatedAt , testing : 123 } ;
const res = await request ( req ) ;
const obj = await getObject ( res . data . objectId ) ;
expect ( obj . createdAt ) . toEqual ( createdAt . iso ) ;
expect ( obj . updatedAt ) . toEqual ( updatedAt . iso ) ;
expect ( obj . testing ) . toEqual ( 123 ) ;
} ) ;
it ( 'cannot set updatedAt dated before createdAt' , async ( ) => {
const createdAt = { _ _type : 'Date' , iso : '2019-01-01T00:00:00.000Z' } ;
const updatedAt = { _ _type : 'Date' , iso : '2018-12-01T00:00:00.000Z' } ;
req . body = { createdAt , updatedAt } ;
try {
await request ( req ) ;
fail ( ) ;
2023-12-10 02:42:40 +01:00
} catch ( err ) {
2023-09-29 13:17:48 -07:00
expect ( err . data . code ) . toEqual ( Parse . Error . VALIDATION _ERROR ) ;
}
} ) ;
it ( 'cannot set updatedAt without createdAt' , async ( ) => {
const updatedAt = { _ _type : 'Date' , iso : '2018-12-01T00:00:00.000Z' } ;
req . body = { updatedAt } ;
const res = await request ( req ) ;
const obj = await getObject ( res . data . objectId ) ;
expect ( obj . updatedAt ) . not . toEqual ( updatedAt . iso ) ;
} ) ;
it ( 'handles bad types for createdAt and updatedAt' , async ( ) => {
const createdAt = 12345 ;
const updatedAt = true ;
req . body = { createdAt , updatedAt } ;
try {
await request ( req ) ;
fail ( ) ;
2023-12-10 02:42:40 +01:00
} catch ( err ) {
2023-09-29 13:17:48 -07:00
expect ( err . data . code ) . toEqual ( Parse . Error . INCORRECT _TYPE ) ;
}
} ) ;
it ( 'cannot set createdAt or updatedAt without maintenance key' , async ( ) => {
const createdAt = { _ _type : 'Date' , iso : '2019-01-01T00:00:00.000Z' } ;
const updatedAt = { _ _type : 'Date' , iso : '2019-02-01T00:00:00.000Z' } ;
req . body = { createdAt , updatedAt } ;
delete req . headers [ 'X-Parse-Maintenance-Key' ] ;
const res = await request ( req ) ;
expect ( res . data . createdAt ) . not . toEqual ( createdAt . iso ) ;
expect ( res . data . updatedAt ) . not . toEqual ( updatedAt . iso ) ;
} ) ;
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( '6c30306f-328c-47f2-88a7-2deffaee997f' ) ( it ) ( 'handles array, object, date' , done => {
2016-12-07 15:17:05 -08:00
const now = new Date ( ) ;
2018-02-17 09:55:30 -05:00
const obj = {
2016-01-28 10:58:12 -08:00
array : [ 1 , 2 , 3 ] ,
2018-09-01 13:58:06 -04:00
object : { foo : 'bar' } ,
2016-05-28 09:25:09 -07:00
date : Parse . _encode ( now ) ,
2016-01-28 10:58:12 -08:00
} ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , 'MyClass' , obj )
. then ( ( ) =>
database . adapter . find (
'MyClass' ,
{
fields : {
array : { type : 'Array' } ,
object : { type : 'Object' } ,
date : { type : 'Date' } ,
} ,
} ,
{ } ,
{ }
)
)
2020-10-25 15:06:58 -05:00
. then ( results => {
2017-06-20 09:15:26 -07:00
expect ( results . length ) . toEqual ( 1 ) ;
2018-02-17 09:55:30 -05:00
const mob = results [ 0 ] ;
2017-06-20 09:15:26 -07:00
expect ( mob . array instanceof Array ) . toBe ( true ) ;
expect ( typeof mob . object ) . toBe ( 'object' ) ;
expect ( mob . date . _ _type ) . toBe ( 'Date' ) ;
expect ( new Date ( mob . date . iso ) . getTime ( ) ) . toBe ( now . getTime ( ) ) ;
done ( ) ;
} ) ;
2016-01-28 10:58:12 -08:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'handles object and subdocument' , done => {
2018-09-01 13:58:06 -04:00
const obj = { subdoc : { foo : 'bar' , wu : 'tan' } } ;
2017-06-14 20:51:41 +02:00
2020-04-22 12:01:11 -04:00
Parse . Cloud . beforeSave ( 'MyClass' , function ( ) {
2017-06-14 20:51:41 +02:00
// this beforeSave trigger should do nothing but can mess with the object
} ) ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , 'MyClass' , obj )
2017-06-20 09:15:26 -07:00
. then ( ( ) => database . adapter . find ( 'MyClass' , { fields : { } } , { } , { } ) )
2020-10-25 15:06:58 -05:00
. then ( results => {
2017-06-20 09:15:26 -07:00
expect ( results . length ) . toEqual ( 1 ) ;
const mob = results [ 0 ] ;
expect ( typeof mob . subdoc ) . toBe ( 'object' ) ;
expect ( mob . subdoc . foo ) . toBe ( 'bar' ) ;
expect ( mob . subdoc . wu ) . toBe ( 'tan' ) ;
expect ( typeof mob . objectId ) . toEqual ( 'string' ) ;
const obj = { 'subdoc.wu' : 'clan' } ;
2020-10-25 15:06:58 -05:00
return rest . update ( config , auth . nobody ( config ) , 'MyClass' , { objectId : mob . objectId } , obj ) ;
2017-06-20 09:15:26 -07:00
} )
. then ( ( ) => database . adapter . find ( 'MyClass' , { fields : { } } , { } , { } ) )
2020-10-25 15:06:58 -05:00
. then ( results => {
2017-06-20 09:15:26 -07:00
expect ( results . length ) . toEqual ( 1 ) ;
const mob = results [ 0 ] ;
expect ( typeof mob . subdoc ) . toBe ( 'object' ) ;
expect ( mob . subdoc . foo ) . toBe ( 'bar' ) ;
expect ( mob . subdoc . wu ) . toBe ( 'clan' ) ;
done ( ) ;
} )
2018-09-24 17:07:51 -04:00
. catch ( done . fail ) ;
2016-02-22 16:45:41 +01:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'handles create on non-existent class when disabled client class creation' , done => {
2018-09-01 13:58:06 -04:00
const customConfig = Object . assign ( { } , config , {
allowClientClassCreation : false ,
} ) ;
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2020-10-25 15:06:58 -05:00
rest . create ( customConfig , auth . nobody ( customConfig ) , 'ClientClassCreation' , { } ) . then (
( ) => {
fail ( 'Should throw an error' ) ;
done ( ) ;
} ,
err => {
expect ( err . code ) . toEqual ( Parse . Error . OPERATION _FORBIDDEN ) ;
2025-11-23 13:51:42 +01:00
expect ( err . message ) . toEqual ( 'Permission denied' ) ;
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( 'This user is not allowed to access ' + 'non-existent class: ClientClassCreation' ) ) ;
2020-10-25 15:06:58 -05:00
done ( ) ;
}
) ;
2016-02-26 22:55:39 +08:00
} ) ;
2019-05-30 11:14:05 -05:00
it ( 'handles create on existent class when disabled client class creation' , async ( ) => {
2018-09-01 13:58:06 -04:00
const customConfig = Object . assign ( { } , config , {
allowClientClassCreation : false ,
} ) ;
2019-05-30 11:14:05 -05:00
const schema = await config . database . loadSchema ( ) ;
2020-10-25 15:06:58 -05:00
const actualSchema = await schema . addClassIfNotExists ( 'ClientClassCreation' , { } ) ;
2019-05-30 11:14:05 -05:00
expect ( actualSchema . className ) . toEqual ( 'ClientClassCreation' ) ;
await schema . reloadData ( { clearCache : true } ) ;
// Should not throw
2020-10-25 15:06:58 -05:00
await rest . create ( customConfig , auth . nobody ( customConfig ) , 'ClientClassCreation' , { } ) ;
2016-06-14 00:21:52 +08:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'handles user signup' , done => {
2018-02-17 09:55:30 -05:00
const user = {
2016-01-28 10:58:12 -08:00
username : 'asdf' ,
password : 'zxcv' ,
foo : 'bar' ,
} ;
2020-10-25 15:06:58 -05:00
rest . create ( config , auth . nobody ( config ) , '_User' , user ) . then ( r => {
2018-09-01 13:58:06 -04:00
expect ( Object . keys ( r . response ) . length ) . toEqual ( 3 ) ;
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
done ( ) ;
} ) ;
2016-01-28 10:58:12 -08:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'handles anonymous user signup' , done => {
2018-02-17 09:55:30 -05:00
const data1 = {
2016-02-11 11:58:51 +00:00
authData : {
anonymous : {
2018-09-01 13:58:06 -04:00
id : '00000000-0000-0000-0000-000000000001' ,
} ,
} ,
2016-02-11 11:58:51 +00:00
} ;
2018-02-17 09:55:30 -05:00
const data2 = {
2016-02-11 11:58:51 +00:00
authData : {
anonymous : {
2018-09-01 13:58:06 -04:00
id : '00000000-0000-0000-0000-000000000002' ,
} ,
} ,
2016-02-11 11:58:51 +00:00
} ;
2018-02-17 09:55:30 -05:00
let username1 ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , '_User' , data1 )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-02-11 11:58:51 +00:00
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
2016-06-27 06:19:37 +03:00
expect ( typeof r . response . username ) . toEqual ( 'string' ) ;
2016-02-11 11:58:51 +00:00
return rest . create ( config , auth . nobody ( config ) , '_User' , data1 ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-02-11 11:58:51 +00:00
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . username ) . toEqual ( 'string' ) ;
expect ( typeof r . response . updatedAt ) . toEqual ( 'string' ) ;
username1 = r . response . username ;
return rest . create ( config , auth . nobody ( config ) , '_User' , data2 ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-02-11 11:58:51 +00:00
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
return rest . create ( config , auth . nobody ( config ) , '_User' , data2 ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-02-11 11:58:51 +00:00
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . username ) . toEqual ( 'string' ) ;
expect ( typeof r . response . updatedAt ) . toEqual ( 'string' ) ;
expect ( r . response . username ) . not . toEqual ( username1 ) ;
done ( ) ;
} ) ;
} ) ;
2016-03-22 21:17:53 -04:00
2020-10-25 15:06:58 -05:00
it ( 'handles anonymous user signup and upgrade to new user' , done => {
2018-02-17 09:55:30 -05:00
const data1 = {
2016-03-22 21:17:53 -04:00
authData : {
anonymous : {
2018-09-01 13:58:06 -04:00
id : '00000000-0000-0000-0000-000000000001' ,
} ,
} ,
2016-03-22 21:17:53 -04:00
} ;
2018-02-17 09:55:30 -05:00
const updatedData = {
2016-03-22 21:17:53 -04:00
authData : { anonymous : null } ,
username : 'hello' ,
2018-09-01 13:58:06 -04:00
password : 'world' ,
} ;
2018-02-17 09:55:30 -05:00
let objectId ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , '_User' , data1 )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-03-22 21:17:53 -04:00
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
objectId = r . response . objectId ;
2018-09-01 13:58:06 -04:00
return auth . getAuthForSessionToken ( {
config ,
sessionToken : r . response . sessionToken ,
} ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( sessionAuth => {
return rest . update ( config , sessionAuth , '_User' , { objectId } , updatedData ) ;
2018-09-01 13:58:06 -04:00
} )
. then ( ( ) => {
2016-11-24 15:47:41 -05:00
return Parse . User . logOut ( ) . then ( ( ) => {
2016-03-22 21:17:53 -04:00
return Parse . User . logIn ( 'hello' , 'world' ) ;
2018-09-01 13:58:06 -04:00
} ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-03-22 21:17:53 -04:00
expect ( r . id ) . toEqual ( objectId ) ;
expect ( r . get ( 'username' ) ) . toEqual ( 'hello' ) ;
done ( ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. catch ( err => {
2016-08-20 16:07:48 -04:00
jfail ( err ) ;
2016-03-22 21:17:53 -04:00
done ( ) ;
2018-09-01 13:58:06 -04:00
} ) ;
2016-03-22 21:17:53 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'handles no anonymous users config' , done => {
2018-02-17 09:55:30 -05:00
const NoAnnonConfig = Object . assign ( { } , config ) ;
2016-11-24 15:47:41 -05:00
NoAnnonConfig . authDataManager . setEnableAnonymousUsers ( false ) ;
2018-02-17 09:55:30 -05:00
const data1 = {
2016-02-15 22:44:41 -05:00
authData : {
anonymous : {
2018-09-01 13:58:06 -04:00
id : '00000000-0000-0000-0000-000000000001' ,
} ,
} ,
2016-02-15 22:44:41 -05:00
} ;
2018-09-01 13:58:06 -04:00
rest . create ( NoAnnonConfig , auth . nobody ( NoAnnonConfig ) , '_User' , data1 ) . then (
( ) => {
fail ( 'Should throw an error' ) ;
done ( ) ;
} ,
2020-10-25 15:06:58 -05:00
err => {
2018-09-01 13:58:06 -04:00
expect ( err . code ) . toEqual ( Parse . Error . UNSUPPORTED _SERVICE ) ;
2020-10-25 15:06:58 -05:00
expect ( err . message ) . toEqual ( 'This authentication method is unsupported.' ) ;
2018-09-01 13:58:06 -04:00
NoAnnonConfig . authDataManager . setEnableAnonymousUsers ( true ) ;
done ( ) ;
}
) ;
2016-02-15 22:44:41 -05:00
} ) ;
2016-02-11 11:58:51 +00:00
2020-10-25 15:06:58 -05:00
it ( 'test facebook signup and login' , done => {
2018-02-17 09:55:30 -05:00
const data = {
2016-01-28 10:58:12 -08:00
authData : {
facebook : {
id : '8675309' ,
2018-09-01 13:58:06 -04:00
access _token : 'jenny' ,
} ,
} ,
2016-01-28 10:58:12 -08:00
} ;
2018-02-17 09:55:30 -05:00
let newUserSignedUpByFacebookObjectId ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , '_User' , data )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-01-28 10:58:12 -08:00
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
2016-03-06 01:32:50 +07:00
newUserSignedUpByFacebookObjectId = r . response . objectId ;
2016-01-28 10:58:12 -08:00
return rest . create ( config , auth . nobody ( config ) , '_User' , data ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-01-28 10:58:12 -08:00
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . username ) . toEqual ( 'string' ) ;
expect ( typeof r . response . updatedAt ) . toEqual ( 'string' ) ;
2016-03-06 01:32:50 +07:00
expect ( r . response . objectId ) . toEqual ( newUserSignedUpByFacebookObjectId ) ;
2018-09-01 13:58:06 -04:00
return rest . find ( config , auth . master ( config ) , '_Session' , {
sessionToken : r . response . sessionToken ,
} ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( response => {
2016-03-06 01:32:50 +07:00
expect ( response . results . length ) . toEqual ( 1 ) ;
2018-02-17 09:55:30 -05:00
const output = response . results [ 0 ] ;
2016-03-06 01:32:50 +07:00
expect ( output . user . objectId ) . toEqual ( newUserSignedUpByFacebookObjectId ) ;
2016-01-28 10:58:12 -08:00
done ( ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. catch ( err => {
2016-08-20 16:07:48 -04:00
jfail ( err ) ;
done ( ) ;
2018-09-01 13:58:06 -04:00
} ) ;
2016-01-28 10:58:12 -08:00
} ) ;
2016-03-11 14:50:33 -05:00
2020-10-25 15:06:58 -05:00
it ( 'stores pointers' , done => {
2016-12-07 15:17:05 -08:00
const obj = {
2016-01-28 10:58:12 -08:00
foo : 'bar' ,
aPointer : {
_ _type : 'Pointer' ,
className : 'JustThePointer' ,
2018-09-01 13:58:06 -04:00
objectId : 'qwerty1234' , // make it 10 chars to match PG storage
} ,
2016-01-28 10:58:12 -08:00
} ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , 'APointerDarkly' , obj )
. then ( ( ) =>
database . adapter . find (
'APointerDarkly' ,
{
fields : {
foo : { type : 'String' } ,
aPointer : { type : 'Pointer' , targetClass : 'JustThePointer' } ,
} ,
} ,
{ } ,
{ }
)
)
2020-10-25 15:06:58 -05:00
. then ( results => {
2017-06-20 09:15:26 -07:00
expect ( results . length ) . toEqual ( 1 ) ;
const output = results [ 0 ] ;
expect ( typeof output . foo ) . toEqual ( 'string' ) ;
expect ( typeof output . _p _aPointer ) . toEqual ( 'undefined' ) ;
expect ( output . _p _aPointer ) . toBeUndefined ( ) ;
expect ( output . aPointer ) . toEqual ( {
_ _type : 'Pointer' ,
className : 'JustThePointer' ,
2018-09-01 13:58:06 -04:00
objectId : 'qwerty1234' ,
2017-06-20 09:15:26 -07:00
} ) ;
done ( ) ;
2016-01-28 10:58:12 -08:00
} ) ;
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'stores pointers to objectIds larger than 10 characters' , done => {
2020-04-22 12:01:11 -04:00
const obj = {
foo : 'bar' ,
aPointer : {
_ _type : 'Pointer' ,
className : 'JustThePointer' ,
objectId : '49F62F92-9B56-46E7-A3D4-BBD14C52F666' ,
} ,
} ;
rest
. create ( config , auth . nobody ( config ) , 'APointerDarkly' , obj )
. then ( ( ) =>
database . adapter . find (
'APointerDarkly' ,
{
fields : {
foo : { type : 'String' } ,
aPointer : { type : 'Pointer' , targetClass : 'JustThePointer' } ,
} ,
} ,
{ } ,
{ }
)
)
2020-10-25 15:06:58 -05:00
. then ( results => {
2020-04-22 12:01:11 -04:00
expect ( results . length ) . toEqual ( 1 ) ;
const output = results [ 0 ] ;
expect ( typeof output . foo ) . toEqual ( 'string' ) ;
expect ( typeof output . _p _aPointer ) . toEqual ( 'undefined' ) ;
expect ( output . _p _aPointer ) . toBeUndefined ( ) ;
expect ( output . aPointer ) . toEqual ( {
_ _type : 'Pointer' ,
className : 'JustThePointer' ,
objectId : '49F62F92-9B56-46E7-A3D4-BBD14C52F666' ,
} ) ;
done ( ) ;
} ) ;
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'cannot set objectId' , done => {
2018-02-17 09:55:30 -05:00
const headers = {
2018-09-24 17:07:51 -04:00
'Content-Type' : 'application/json' ,
2016-01-28 10:58:12 -08:00
'X-Parse-Application-Id' : 'test' ,
2018-09-01 13:58:06 -04:00
'X-Parse-REST-API-Key' : 'rest' ,
2016-01-28 10:58:12 -08:00
} ;
2018-09-24 17:07:51 -04:00
request ( {
headers : headers ,
method : 'POST' ,
url : 'http://localhost:8378/1/classes/TestObject' ,
body : JSON . stringify ( {
foo : 'bar' ,
objectId : 'hello' ,
} ) ,
2020-10-25 15:06:58 -05:00
} ) . then ( fail , response => {
2018-09-24 17:07:51 -04:00
const b = response . data ;
expect ( b . code ) . toEqual ( 105 ) ;
expect ( b . error ) . toEqual ( 'objectId is an invalid field name.' ) ;
done ( ) ;
} ) ;
2016-01-28 10:58:12 -08:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'cannot set id' , done => {
2019-06-11 13:40:34 -05:00
const headers = {
'Content-Type' : 'application/json' ,
'X-Parse-Application-Id' : 'test' ,
'X-Parse-REST-API-Key' : 'rest' ,
} ;
request ( {
headers : headers ,
method : 'POST' ,
url : 'http://localhost:8378/1/classes/TestObject' ,
body : JSON . stringify ( {
foo : 'bar' ,
id : 'hello' ,
} ) ,
2020-10-25 15:06:58 -05:00
} ) . then ( fail , response => {
2019-06-11 13:40:34 -05:00
const b = response . data ;
expect ( b . code ) . toEqual ( 105 ) ;
expect ( b . error ) . toEqual ( 'id is an invalid field name.' ) ;
done ( ) ;
} ) ;
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'test default session length' , done => {
2018-02-17 09:55:30 -05:00
const user = {
2016-04-02 11:36:47 -04:00
username : 'asdf' ,
password : 'zxcv' ,
foo : 'bar' ,
} ;
2018-02-17 09:55:30 -05:00
const now = new Date ( ) ;
2016-04-02 11:36:47 -04:00
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , '_User' , user )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-04-02 11:36:47 -04:00
expect ( Object . keys ( r . response ) . length ) . toEqual ( 3 ) ;
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
2018-09-01 13:58:06 -04:00
return rest . find ( config , auth . master ( config ) , '_Session' , {
sessionToken : r . response . sessionToken ,
} ) ;
2016-04-02 11:36:47 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-04-02 11:36:47 -04:00
expect ( r . results . length ) . toEqual ( 1 ) ;
2018-02-17 09:55:30 -05:00
const session = r . results [ 0 ] ;
const actual = new Date ( session . expiresAt . iso ) ;
2018-09-01 13:58:06 -04:00
const expected = new Date ( now . getTime ( ) + 1000 * 3600 * 24 * 365 ) ;
2016-04-02 11:36:47 -04:00
2021-02-19 11:31:06 -08:00
expect ( Math . abs ( actual - expected ) <= jasmine . DEFAULT _TIMEOUT _INTERVAL ) . toEqual ( true ) ;
2016-04-02 11:36:47 -04:00
done ( ) ;
} ) ;
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'test specified session length' , done => {
2018-02-17 09:55:30 -05:00
const user = {
2016-04-02 11:36:47 -04:00
username : 'asdf' ,
password : 'zxcv' ,
foo : 'bar' ,
} ;
2018-02-17 09:55:30 -05:00
const sessionLength = 3600 , // 1 Hour ahead
2016-11-24 15:47:41 -05:00
now = new Date ( ) ; // For reference later
2016-04-02 11:36:47 -04:00
config . sessionLength = sessionLength ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , '_User' , user )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-04-02 11:36:47 -04:00
expect ( Object . keys ( r . response ) . length ) . toEqual ( 3 ) ;
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
2018-09-01 13:58:06 -04:00
return rest . find ( config , auth . master ( config ) , '_Session' , {
sessionToken : r . response . sessionToken ,
} ) ;
2016-04-02 11:36:47 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-04-02 11:36:47 -04:00
expect ( r . results . length ) . toEqual ( 1 ) ;
2018-02-17 09:55:30 -05:00
const session = r . results [ 0 ] ;
const actual = new Date ( session . expiresAt . iso ) ;
2018-09-01 13:58:06 -04:00
const expected = new Date ( now . getTime ( ) + sessionLength * 1000 ) ;
2016-04-02 11:36:47 -04:00
2021-02-19 11:31:06 -08:00
expect ( Math . abs ( actual - expected ) <= jasmine . DEFAULT _TIMEOUT _INTERVAL ) . toEqual ( true ) ;
2016-04-02 11:36:47 -04:00
2016-08-20 16:07:48 -04:00
done ( ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. catch ( err => {
2016-08-20 16:07:48 -04:00
jfail ( err ) ;
2016-04-02 11:36:47 -04:00
done ( ) ;
} ) ;
} ) ;
2016-05-06 20:50:45 +01:00
2020-10-25 15:06:58 -05:00
it ( 'can create a session with no expiration' , done => {
2018-02-17 09:55:30 -05:00
const user = {
2016-05-06 20:50:45 +01:00
username : 'asdf' ,
password : 'zxcv' ,
2018-09-01 13:58:06 -04:00
foo : 'bar' ,
2016-05-06 20:50:45 +01:00
} ;
config . expireInactiveSessions = false ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . nobody ( config ) , '_User' , user )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-05-06 20:50:45 +01:00
expect ( Object . keys ( r . response ) . length ) . toEqual ( 3 ) ;
expect ( typeof r . response . objectId ) . toEqual ( 'string' ) ;
expect ( typeof r . response . createdAt ) . toEqual ( 'string' ) ;
expect ( typeof r . response . sessionToken ) . toEqual ( 'string' ) ;
2018-09-01 13:58:06 -04:00
return rest . find ( config , auth . master ( config ) , '_Session' , {
sessionToken : r . response . sessionToken ,
} ) ;
2016-05-06 20:50:45 +01:00
} )
2020-10-25 15:06:58 -05:00
. then ( r => {
2016-05-06 20:50:45 +01:00
expect ( r . results . length ) . toEqual ( 1 ) ;
2018-02-17 09:55:30 -05:00
const session = r . results [ 0 ] ;
2016-05-06 20:50:45 +01:00
expect ( session . expiresAt ) . toBeUndefined ( ) ;
done ( ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. catch ( err => {
2016-08-20 16:07:48 -04:00
console . error ( err ) ;
fail ( err ) ;
done ( ) ;
2018-09-01 13:58:06 -04:00
} ) ;
2016-05-06 20:50:45 +01:00
} ) ;
2017-06-13 16:36:40 +02:00
2020-10-25 15:06:58 -05:00
it ( 'can create object in volatileClasses if masterKey' , done => {
2018-09-01 13:58:06 -04:00
rest
. create ( config , auth . master ( config ) , '_PushStatus' , { } )
2020-10-25 15:06:58 -05:00
. then ( r => {
2017-06-13 16:36:40 +02:00
expect ( r . response . objectId . length ) . toBe ( 10 ) ;
} )
. then ( ( ) => {
2020-10-25 15:06:58 -05:00
rest . create ( config , auth . master ( config ) , '_JobStatus' , { } ) . then ( r => {
2018-09-01 13:58:06 -04:00
expect ( r . response . objectId . length ) . toBe ( 10 ) ;
done ( ) ;
} ) ;
} ) ;
2017-06-13 16:36:40 +02:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'cannot create object in volatileClasses if not masterKey' , done => {
2017-06-13 16:36:40 +02:00
Promise . resolve ( )
. then ( ( ) => {
2018-09-01 13:58:06 -04:00
return rest . create ( config , auth . nobody ( config ) , '_PushStatus' , { } ) ;
2017-06-13 16:36:40 +02:00
} )
2020-10-25 15:06:58 -05:00
. catch ( error => {
2017-06-13 16:36:40 +02:00
expect ( error . code ) . toEqual ( 119 ) ;
done ( ) ;
2018-09-01 13:58:06 -04:00
} ) ;
2017-09-18 14:53:11 -04:00
} ) ;
2023-09-04 14:19:48 +02:00
it ( 'cannot get object in volatileClasses if not masterKey through pointer' , async ( ) => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2023-09-04 14:19:48 +02:00
const masterKeyOnlyClassObject = new Parse . Object ( '_PushStatus' ) ;
await masterKeyOnlyClassObject . save ( null , { useMasterKey : true } ) ;
const obj2 = new Parse . Object ( 'TestObject' ) ;
// Anyone is can basically create a pointer to any object
// or some developers can use master key in some hook to link
// private objects to standard objects
obj2 . set ( 'pointer' , masterKeyOnlyClassObject ) ;
await obj2 . save ( ) ;
const query = new Parse . Query ( 'TestObject' ) ;
query . include ( 'pointer' ) ;
await expectAsync ( query . get ( obj2 . id ) ) . toBeRejectedWithError (
2025-11-23 13:51:42 +01:00
'Permission denied'
2023-09-04 14:19:48 +02:00
) ;
2025-11-23 13:51:42 +01:00
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "Clients aren't allowed to perform the get operation on the _PushStatus collection." ) ) ;
2023-09-04 14:19:48 +02:00
} ) ;
2024-08-16 15:21:56 -04:00
it _id ( '3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc' ) ( it ) ( 'cannot get object in _GlobalConfig if not masterKey through pointer' , async ( ) => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2023-09-04 14:19:48 +02:00
await Parse . Config . save ( { privateData : 'secret' } , { privateData : true } ) ;
const obj2 = new Parse . Object ( 'TestObject' ) ;
obj2 . set ( 'globalConfigPointer' , {
_ _type : 'Pointer' ,
className : '_GlobalConfig' ,
objectId : 1 ,
} ) ;
await obj2 . save ( ) ;
const query = new Parse . Query ( 'TestObject' ) ;
query . include ( 'globalConfigPointer' ) ;
await expectAsync ( query . get ( obj2 . id ) ) . toBeRejectedWithError (
2025-11-23 13:51:42 +01:00
'Permission denied'
2023-09-04 14:19:48 +02:00
) ;
2025-11-23 13:51:42 +01:00
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "Clients aren't allowed to perform the get operation on the _GlobalConfig collection." ) ) ;
2023-09-04 14:19:48 +02:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'locks down session' , done => {
2017-09-18 14:53:11 -04:00
let currentUser ;
2018-09-01 13:58:06 -04:00
Parse . User . signUp ( 'foo' , 'bar' )
2020-10-25 15:06:58 -05:00
. then ( user => {
2018-09-01 13:58:06 -04:00
currentUser = user ;
const sessionToken = user . getSessionToken ( ) ;
const headers = {
2018-09-24 17:07:51 -04:00
'Content-Type' : 'application/json' ,
2018-09-01 13:58:06 -04:00
'X-Parse-Application-Id' : 'test' ,
'X-Parse-REST-API-Key' : 'rest' ,
'X-Parse-Session-Token' : sessionToken ,
} ;
let sessionId ;
2018-09-24 17:07:51 -04:00
return request ( {
headers : headers ,
url : 'http://localhost:8378/1/sessions/me' ,
} )
2020-10-25 15:06:58 -05:00
. then ( response => {
2018-09-24 17:07:51 -04:00
sessionId = response . data . objectId ;
return request ( {
2018-09-01 13:58:06 -04:00
headers ,
2018-09-24 17:07:51 -04:00
method : 'PUT' ,
2018-09-01 13:58:06 -04:00
url : 'http://localhost:8378/1/sessions/' + sessionId ,
2018-09-24 17:07:51 -04:00
body : {
2018-09-01 13:58:06 -04:00
installationId : 'yolo' ,
} ,
} ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( done . fail , res => {
2018-09-24 17:07:51 -04:00
expect ( res . status ) . toBe ( 400 ) ;
expect ( res . data . code ) . toBe ( 105 ) ;
return request ( {
2018-09-01 13:58:06 -04:00
headers ,
2018-09-24 17:07:51 -04:00
method : 'PUT' ,
2018-09-01 13:58:06 -04:00
url : 'http://localhost:8378/1/sessions/' + sessionId ,
2018-09-24 17:07:51 -04:00
body : {
2018-09-01 13:58:06 -04:00
sessionToken : 'yolo' ,
} ,
} ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( done . fail , res => {
2018-09-24 17:07:51 -04:00
expect ( res . status ) . toBe ( 400 ) ;
expect ( res . data . code ) . toBe ( 105 ) ;
2018-09-01 13:58:06 -04:00
return Parse . User . signUp ( 'other' , 'user' ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( otherUser => {
2018-09-01 13:58:06 -04:00
const user = new Parse . User ( ) ;
user . id = otherUser . id ;
2018-09-24 17:07:51 -04:00
return request ( {
2018-09-01 13:58:06 -04:00
headers ,
2018-09-24 17:07:51 -04:00
method : 'PUT' ,
2018-09-01 13:58:06 -04:00
url : 'http://localhost:8378/1/sessions/' + sessionId ,
2018-09-24 17:07:51 -04:00
body : {
2018-09-01 13:58:06 -04:00
user : Parse . _encode ( user ) ,
} ,
} ) ;
} )
2020-10-25 15:06:58 -05:00
. then ( done . fail , res => {
2018-09-24 17:07:51 -04:00
expect ( res . status ) . toBe ( 400 ) ;
expect ( res . data . code ) . toBe ( 105 ) ;
2018-09-01 13:58:06 -04:00
const user = new Parse . User ( ) ;
user . id = currentUser . id ;
2018-09-24 17:07:51 -04:00
return request ( {
2018-09-01 13:58:06 -04:00
headers ,
2018-09-24 17:07:51 -04:00
method : 'PUT' ,
2018-09-01 13:58:06 -04:00
url : 'http://localhost:8378/1/sessions/' + sessionId ,
2018-09-24 17:07:51 -04:00
body : {
2018-09-01 13:58:06 -04:00
user : Parse . _encode ( user ) ,
} ,
} ) ;
} )
. then ( done )
. catch ( done . fail ) ;
} )
. catch ( done . fail ) ;
2017-09-18 14:53:11 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'sets current user in new sessions' , done => {
2017-09-18 14:53:11 -04:00
let currentUser ;
Parse . User . signUp ( 'foo' , 'bar' )
2020-10-25 15:06:58 -05:00
. then ( user => {
2017-09-18 14:53:11 -04:00
currentUser = user ;
const sessionToken = user . getSessionToken ( ) ;
const headers = {
'X-Parse-Application-Id' : 'test' ,
'X-Parse-REST-API-Key' : 'rest' ,
'X-Parse-Session-Token' : sessionToken ,
2018-09-24 17:07:51 -04:00
'Content-Type' : 'application/json' ,
2017-09-18 14:53:11 -04:00
} ;
2018-09-24 17:07:51 -04:00
return request ( {
2017-09-18 14:53:11 -04:00
headers ,
2018-09-24 17:07:51 -04:00
method : 'POST' ,
2017-09-18 14:53:11 -04:00
url : 'http://localhost:8378/1/sessions' ,
2018-09-01 13:58:06 -04:00
body : {
user : { _ _type : 'Pointer' , className : '_User' , objectId : 'fakeId' } ,
} ,
} ) ;
2017-09-18 14:53:11 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( response => {
2018-09-24 17:07:51 -04:00
if ( response . data . user . objectId === currentUser . id ) {
2017-09-18 14:53:11 -04:00
return done ( ) ;
} else {
return done . fail ( ) ;
}
} )
. catch ( done . fail ) ;
2018-09-01 13:58:06 -04:00
} ) ;
2016-01-28 10:58:12 -08:00
} ) ;
2016-11-25 23:20:06 +09:00
describe ( 'rest update' , ( ) => {
2020-10-25 15:06:58 -05:00
it ( 'ignores createdAt' , done => {
2018-02-17 09:55:30 -05:00
const config = Config . get ( 'test' ) ;
2016-11-25 23:20:06 +09:00
const nobody = auth . nobody ( config ) ;
const className = 'Foo' ;
const newCreatedAt = new Date ( '1970-01-01T00:00:00.000Z' ) ;
2018-09-01 13:58:06 -04:00
rest
. create ( config , nobody , className , { } )
2020-10-25 15:06:58 -05:00
. then ( res => {
2018-09-01 13:58:06 -04:00
const objectId = res . response . objectId ;
const restObject = {
createdAt : { _ _type : 'Date' , iso : newCreatedAt } , // should be ignored
2016-11-25 23:20:06 +09:00
} ;
2018-09-01 13:58:06 -04:00
2020-10-25 15:06:58 -05:00
return rest . update ( config , nobody , className , { objectId } , restObject ) . then ( ( ) => {
const restWhere = {
objectId : objectId ,
} ;
return rest . find ( config , nobody , className , restWhere , { } ) ;
} ) ;
2018-09-01 13:58:06 -04:00
} )
2020-10-25 15:06:58 -05:00
. then ( res2 => {
2018-09-01 13:58:06 -04:00
const updatedObject = res2 . results [ 0 ] ;
expect ( new Date ( updatedObject . createdAt ) ) . not . toEqual ( newCreatedAt ) ;
done ( ) ;
} )
. then ( done )
. catch ( done . fail ) ;
2016-11-25 23:20:06 +09:00
} ) ;
2017-10-26 15:35:07 -04:00
} ) ;
describe ( 'read-only masterKey' , ( ) => {
2025-11-23 13:51:42 +01:00
let loggerErrorSpy ;
let logger ;
beforeEach ( ( ) => {
logger = require ( '../lib/logger' ) . default ;
loggerErrorSpy = spyOn ( logger , 'error' ) . and . callThrough ( ) ;
} ) ;
2017-10-26 15:35:07 -04:00
it ( 'properly throws on rest.create, rest.update and rest.del' , ( ) => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2017-10-26 15:35:07 -04:00
const config = Config . get ( 'test' ) ;
const readOnly = auth . readOnly ( config ) ;
expect ( ( ) => {
2018-09-01 13:58:06 -04:00
rest . create ( config , readOnly , 'AnObject' , { } ) ;
} ) . toThrow (
new Parse . Error (
Parse . Error . OPERATION _FORBIDDEN ,
2025-11-23 13:51:42 +01:00
'Permission denied'
2018-09-01 13:58:06 -04:00
)
) ;
2025-11-23 13:51:42 +01:00
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to perform the create operation." ) ) ;
2017-10-26 15:35:07 -04:00
expect ( ( ) => {
2018-09-01 13:58:06 -04:00
rest . update ( config , readOnly , 'AnObject' , { } ) ;
2017-10-26 15:35:07 -04:00
} ) . toThrow ( ) ;
expect ( ( ) => {
2018-09-01 13:58:06 -04:00
rest . del ( config , readOnly , 'AnObject' , { } ) ;
2017-10-26 15:35:07 -04:00
} ) . toThrow ( ) ;
} ) ;
2021-03-13 09:05:22 -06:00
it ( 'properly blocks writes' , async ( ) => {
await reconfigureServer ( {
2018-09-01 13:58:06 -04:00
readOnlyMasterKey : 'yolo-read-only' ,
2021-03-13 09:05:22 -06:00
} ) ;
2025-11-23 13:51:42 +01:00
// Need to be re required because reconfigureServer resets the logger
const logger2 = require ( '../lib/logger' ) . default ;
loggerErrorSpy = spyOn ( logger2 , 'error' ) . and . callThrough ( ) ;
2021-03-13 09:05:22 -06:00
try {
await request ( {
url : ` ${ Parse . serverURL } /classes/MyYolo ` ,
method : 'POST' ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-Master-Key' : 'yolo-read-only' ,
'Content-Type' : 'application/json' ,
} ,
body : { foo : 'bar' } ,
2017-10-26 15:35:07 -04:00
} ) ;
2021-03-13 09:05:22 -06:00
fail ( ) ;
} catch ( res ) {
expect ( res . data . code ) . toBe ( Parse . Error . OPERATION _FORBIDDEN ) ;
expect ( res . data . error ) . toBe (
2025-11-23 13:51:42 +01:00
'Permission denied'
2021-03-13 09:05:22 -06:00
) ;
2025-11-23 13:51:42 +01:00
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to perform the create operation." ) ) ;
2021-03-13 09:05:22 -06:00
}
await reconfigureServer ( ) ;
} ) ;
it ( 'should throw when masterKey and readOnlyMasterKey are the same' , async ( ) => {
try {
await reconfigureServer ( {
masterKey : 'yolo' ,
readOnlyMasterKey : 'yolo' ,
2018-09-01 13:58:06 -04:00
} ) ;
2021-03-13 09:05:22 -06:00
fail ( ) ;
} catch ( err ) {
expect ( err ) . toEqual ( new Error ( 'masterKey and readOnlyMasterKey should be different' ) ) ;
}
await reconfigureServer ( ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2023-01-09 08:02:12 +11:00
it ( 'should throw when masterKey and maintenanceKey are the same' , async ( ) => {
await expectAsync (
reconfigureServer ( {
masterKey : 'yolo' ,
maintenanceKey : 'yolo' ,
} )
) . toBeRejectedWith ( new Error ( 'masterKey and maintenanceKey should be different' ) ) ;
} ) ;
2017-10-26 15:35:07 -04:00
it ( 'should throw when trying to create RestWrite' , ( ) => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2017-10-26 15:35:07 -04:00
const config = Config . get ( 'test' ) ;
expect ( ( ) => {
new RestWrite ( config , auth . readOnly ( config ) ) ;
2018-09-01 13:58:06 -04:00
} ) . toThrow (
2025-11-23 13:51:42 +01:00
new Parse . Error ( Parse . Error . OPERATION _FORBIDDEN , 'Permission denied' )
2018-09-01 13:58:06 -04:00
) ;
2025-11-23 13:51:42 +01:00
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "Cannot perform a write operation when using readOnlyMasterKey" ) ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'should throw when trying to create schema' , done => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2021-03-13 09:05:22 -06:00
request ( {
2018-09-24 17:07:51 -04:00
method : 'POST' ,
url : ` ${ Parse . serverURL } /schemas ` ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-Master-Key' : 'read-only-test' ,
'Content-Type' : 'application/json' ,
} ,
json : { } ,
} )
2018-09-01 13:58:06 -04:00
. then ( done . fail )
2020-10-25 15:06:58 -05:00
. catch ( res => {
2018-09-24 17:07:51 -04:00
expect ( res . data . code ) . toBe ( Parse . Error . OPERATION _FORBIDDEN ) ;
2025-11-23 13:51:42 +01:00
expect ( res . data . error ) . toBe ( 'Permission denied' ) ;
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to create a schema." ) ) ;
2018-09-01 13:58:06 -04:00
done ( ) ;
} ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'should throw when trying to create schema with a name' , done => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2021-03-13 09:05:22 -06:00
request ( {
2018-09-24 17:07:51 -04:00
url : ` ${ Parse . serverURL } /schemas/MyClass ` ,
method : 'POST' ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-Master-Key' : 'read-only-test' ,
'Content-Type' : 'application/json' ,
} ,
json : { } ,
} )
2018-09-01 13:58:06 -04:00
. then ( done . fail )
2020-10-25 15:06:58 -05:00
. catch ( res => {
2018-09-24 17:07:51 -04:00
expect ( res . data . code ) . toBe ( Parse . Error . OPERATION _FORBIDDEN ) ;
2025-11-23 13:51:42 +01:00
expect ( res . data . error ) . toBe ( 'Permission denied' ) ;
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to create a schema." ) ) ;
2018-09-01 13:58:06 -04:00
done ( ) ;
} ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'should throw when trying to update schema' , done => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2021-03-13 09:05:22 -06:00
request ( {
2018-09-24 17:07:51 -04:00
url : ` ${ Parse . serverURL } /schemas/MyClass ` ,
method : 'PUT' ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-Master-Key' : 'read-only-test' ,
'Content-Type' : 'application/json' ,
} ,
json : { } ,
} )
2018-09-01 13:58:06 -04:00
. then ( done . fail )
2020-10-25 15:06:58 -05:00
. catch ( res => {
2018-09-24 17:07:51 -04:00
expect ( res . data . code ) . toBe ( Parse . Error . OPERATION _FORBIDDEN ) ;
2025-11-23 13:51:42 +01:00
expect ( res . data . error ) . toBe ( 'Permission denied' ) ;
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to update a schema." ) ) ;
2018-09-01 13:58:06 -04:00
done ( ) ;
} ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'should throw when trying to delete schema' , done => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2021-03-13 09:05:22 -06:00
request ( {
2018-09-24 17:07:51 -04:00
url : ` ${ Parse . serverURL } /schemas/MyClass ` ,
method : 'DELETE' ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-Master-Key' : 'read-only-test' ,
'Content-Type' : 'application/json' ,
} ,
json : { } ,
} )
2018-09-01 13:58:06 -04:00
. then ( done . fail )
2020-10-25 15:06:58 -05:00
. catch ( res => {
2018-09-24 17:07:51 -04:00
expect ( res . data . code ) . toBe ( Parse . Error . OPERATION _FORBIDDEN ) ;
2025-11-23 13:51:42 +01:00
expect ( res . data . error ) . toBe ( 'Permission denied' ) ;
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to delete a schema." ) ) ;
2018-09-01 13:58:06 -04:00
done ( ) ;
} ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'should throw when trying to update the global config' , done => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2021-03-13 09:05:22 -06:00
request ( {
2018-09-24 17:07:51 -04:00
url : ` ${ Parse . serverURL } /config ` ,
method : 'PUT' ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-Master-Key' : 'read-only-test' ,
'Content-Type' : 'application/json' ,
} ,
json : { } ,
} )
2018-09-01 13:58:06 -04:00
. then ( done . fail )
2020-10-25 15:06:58 -05:00
. catch ( res => {
2018-09-24 17:07:51 -04:00
expect ( res . data . code ) . toBe ( Parse . Error . OPERATION _FORBIDDEN ) ;
2025-11-23 13:51:42 +01:00
expect ( res . data . error ) . toBe ( 'Permission denied' ) ;
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to update the config." ) ) ;
2018-09-01 13:58:06 -04:00
done ( ) ;
} ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2020-10-25 15:06:58 -05:00
it ( 'should throw when trying to send push' , done => {
2025-11-23 13:51:42 +01:00
loggerErrorSpy . calls . reset ( ) ;
2021-03-13 09:05:22 -06:00
request ( {
2018-09-24 17:07:51 -04:00
url : ` ${ Parse . serverURL } /push ` ,
method : 'POST' ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-Master-Key' : 'read-only-test' ,
'Content-Type' : 'application/json' ,
} ,
json : { } ,
} )
2018-09-01 13:58:06 -04:00
. then ( done . fail )
2020-10-25 15:06:58 -05:00
. catch ( res => {
2018-09-24 17:07:51 -04:00
expect ( res . data . code ) . toBe ( Parse . Error . OPERATION _FORBIDDEN ) ;
expect ( res . data . error ) . toBe (
2025-11-23 13:51:42 +01:00
'Permission denied'
2018-09-01 13:58:06 -04:00
) ;
2025-11-23 13:51:42 +01:00
expect ( loggerErrorSpy ) . toHaveBeenCalledWith ( 'Sanitized error:' , jasmine . stringContaining ( "read-only masterKey isn't allowed to send push notifications." ) ) ;
2018-09-01 13:58:06 -04:00
done ( ) ;
} ) ;
2017-10-26 15:35:07 -04:00
} ) ;
2016-11-25 23:20:06 +09:00
} ) ;
2025-10-14 20:16:31 +02:00
describe ( 'rest context' , ( ) => {
it ( 'should support dependency injection on rest api' , async ( ) => {
const requestContextMiddleware = ( req , res , next ) => {
req . config . aCustomController = 'aCustomController' ;
next ( ) ;
} ;
let called
await reconfigureServer ( { requestContextMiddleware } ) ;
Parse . Cloud . beforeSave ( '_User' , request => {
expect ( request . config . aCustomController ) . toEqual ( 'aCustomController' ) ;
called = true ;
} ) ;
const user = new Parse . User ( ) ;
user . setUsername ( 'test' ) ;
user . setPassword ( 'test' ) ;
await user . signUp ( ) ;
expect ( called ) . toBe ( true ) ;
} ) ;
} ) ;