* fix: Set falsy values as default to schema fields * fix: Promise is not being returned in classUpdate method
1830 lines
51 KiB
JavaScript
1830 lines
51 KiB
JavaScript
'use strict';
|
|
|
|
const Config = require('../lib/Config');
|
|
const SchemaController = require('../lib/Controllers/SchemaController');
|
|
const dd = require('deep-diff');
|
|
const TestUtils = require('../lib/TestUtils');
|
|
|
|
let config;
|
|
|
|
const hasAllPODobject = () => {
|
|
const obj = new Parse.Object('HasAllPOD');
|
|
obj.set('aNumber', 5);
|
|
obj.set('aString', 'string');
|
|
obj.set('aBool', true);
|
|
obj.set('aDate', new Date());
|
|
obj.set('aObject', { k1: 'value', k2: true, k3: 5 });
|
|
obj.set('aArray', ['contents', true, 5]);
|
|
obj.set('aGeoPoint', new Parse.GeoPoint({ latitude: 0, longitude: 0 }));
|
|
obj.set(
|
|
'aFile',
|
|
new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' })
|
|
);
|
|
return obj;
|
|
};
|
|
|
|
describe('SchemaController', () => {
|
|
beforeEach(() => {
|
|
config = Config.get('test');
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await config.database.schemaCache.clear();
|
|
await TestUtils.destroyAllDataPermanently(false);
|
|
});
|
|
|
|
it('can validate one object', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
return schema.validateObject('TestObject', { a: 1, b: 'yo', c: false });
|
|
})
|
|
.then(
|
|
() => {
|
|
done();
|
|
},
|
|
error => {
|
|
jfail(error);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can validate one object with dot notation', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
return schema.validateObject('TestObjectWithSubDoc', {
|
|
x: false,
|
|
y: 'YY',
|
|
z: 1,
|
|
'aObject.k1': 'newValue',
|
|
});
|
|
})
|
|
.then(
|
|
() => {
|
|
done();
|
|
},
|
|
error => {
|
|
jfail(error);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can validate two objects in a row', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
return schema.validateObject('Foo', { x: true, y: 'yyy', z: 0 });
|
|
})
|
|
.then(schema => {
|
|
return schema.validateObject('Foo', { x: false, y: 'YY', z: 1 });
|
|
})
|
|
.then(() => {
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can validate Relation object', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
return schema.validateObject('Stuff', {
|
|
aRelation: { __type: 'Relation', className: 'Stuff' },
|
|
});
|
|
})
|
|
.then(schema => {
|
|
return schema
|
|
.validateObject('Stuff', {
|
|
aRelation: { __type: 'Pointer', className: 'Stuff' },
|
|
})
|
|
.then(
|
|
() => {
|
|
done.fail('expected invalidity');
|
|
},
|
|
() => done()
|
|
);
|
|
}, done.fail);
|
|
});
|
|
|
|
it('rejects inconsistent types', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
return schema.validateObject('Stuff', { bacon: 7 });
|
|
})
|
|
.then(schema => {
|
|
return schema.validateObject('Stuff', { bacon: 'z' });
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('expected invalidity');
|
|
done();
|
|
},
|
|
() => done()
|
|
);
|
|
});
|
|
|
|
it('updates when new fields are added', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
return schema.validateObject('Stuff', { bacon: 7 });
|
|
})
|
|
.then(schema => {
|
|
return schema.validateObject('Stuff', { sausage: 8 });
|
|
})
|
|
.then(schema => {
|
|
return schema.validateObject('Stuff', { sausage: 'ate' });
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('expected invalidity');
|
|
done();
|
|
},
|
|
() => done()
|
|
);
|
|
});
|
|
|
|
it('class-level permissions test find', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
find: {},
|
|
});
|
|
})
|
|
.then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Class permissions should have rejected this query.');
|
|
done();
|
|
},
|
|
() => {
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('class-level permissions test user', done => {
|
|
let user;
|
|
createTestUser()
|
|
.then(u => {
|
|
user = u;
|
|
return config.database.loadSchema();
|
|
})
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
const find = {};
|
|
find[user.id] = true;
|
|
return schema.setPermissions('Stuff', {
|
|
find: find,
|
|
});
|
|
})
|
|
.then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find();
|
|
})
|
|
.then(
|
|
() => {
|
|
done();
|
|
},
|
|
() => {
|
|
fail('Class permissions should have allowed this query.');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('class-level permissions test get', done => {
|
|
let obj;
|
|
createTestUser().then(user => {
|
|
return (
|
|
config.database
|
|
.loadSchema()
|
|
// Create a valid class
|
|
.then(schema => schema.validateObject('Stuff', { foo: 'bar' }))
|
|
.then(schema => {
|
|
const find = {};
|
|
const get = {};
|
|
get[user.id] = true;
|
|
return schema.setPermissions('Stuff', {
|
|
create: { '*': true },
|
|
find: find,
|
|
get: get,
|
|
});
|
|
})
|
|
.then(() => {
|
|
obj = new Parse.Object('Stuff');
|
|
obj.set('foo', 'bar');
|
|
return obj.save();
|
|
})
|
|
.then(o => {
|
|
obj = o;
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Class permissions should have rejected this query.');
|
|
done();
|
|
},
|
|
() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.get(obj.id).then(
|
|
() => {
|
|
done();
|
|
},
|
|
() => {
|
|
fail('Class permissions should have allowed this get query');
|
|
done();
|
|
}
|
|
);
|
|
}
|
|
)
|
|
);
|
|
});
|
|
});
|
|
|
|
it('class-level permissions test count', done => {
|
|
let obj;
|
|
return (
|
|
config.database
|
|
.loadSchema()
|
|
// Create a valid class
|
|
.then(schema => schema.validateObject('Stuff', { foo: 'bar' }))
|
|
.then(schema => {
|
|
const count = {};
|
|
return schema.setPermissions('Stuff', {
|
|
create: { '*': true },
|
|
find: { '*': true },
|
|
count: count,
|
|
});
|
|
})
|
|
.then(() => {
|
|
obj = new Parse.Object('Stuff');
|
|
obj.set('foo', 'bar');
|
|
return obj.save();
|
|
})
|
|
.then(o => {
|
|
obj = o;
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find();
|
|
})
|
|
.then(results => {
|
|
expect(results.length).toBe(1);
|
|
const query = new Parse.Query('Stuff');
|
|
return query.count();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Class permissions should have rejected this query.');
|
|
},
|
|
err => {
|
|
expect(err.message).toEqual(
|
|
'Permission denied for action count on class Stuff.'
|
|
);
|
|
done();
|
|
}
|
|
)
|
|
);
|
|
});
|
|
|
|
it('can add classes without needing an object', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'String' },
|
|
})
|
|
)
|
|
.then(actualSchema => {
|
|
const expectedSchema = {
|
|
className: 'NewClass',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
foo: { type: 'String' },
|
|
},
|
|
classLevelPermissions: {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
},
|
|
};
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
done();
|
|
})
|
|
.catch(error => {
|
|
fail('Error creating class: ' + JSON.stringify(error));
|
|
});
|
|
});
|
|
|
|
it('can update classes without needing an object', done => {
|
|
const levelPermissions = {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
};
|
|
config.database.loadSchema().then(schema => {
|
|
schema
|
|
.validateObject('NewClass', { foo: 2 })
|
|
.then(() => schema.reloadData())
|
|
.then(() =>
|
|
schema.updateClass(
|
|
'NewClass',
|
|
{
|
|
fooOne: { type: 'Number' },
|
|
fooTwo: { type: 'Array' },
|
|
fooThree: { type: 'Date' },
|
|
fooFour: { type: 'Object' },
|
|
fooFive: { type: 'Relation', targetClass: '_User' },
|
|
fooSix: { type: 'String' },
|
|
fooSeven: { type: 'Object' },
|
|
fooEight: { type: 'String' },
|
|
fooNine: { type: 'String' },
|
|
fooTeen: { type: 'Number' },
|
|
fooEleven: { type: 'String' },
|
|
fooTwelve: { type: 'String' },
|
|
fooThirteen: { type: 'String' },
|
|
fooFourteen: { type: 'String' },
|
|
fooFifteen: { type: 'String' },
|
|
fooSixteen: { type: 'String' },
|
|
fooEighteen: { type: 'String' },
|
|
fooNineteen: { type: 'String' },
|
|
},
|
|
levelPermissions,
|
|
{},
|
|
config.database
|
|
)
|
|
)
|
|
.then(actualSchema => {
|
|
const expectedSchema = {
|
|
className: 'NewClass',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
foo: { type: 'Number' },
|
|
fooOne: { type: 'Number' },
|
|
fooTwo: { type: 'Array' },
|
|
fooThree: { type: 'Date' },
|
|
fooFour: { type: 'Object' },
|
|
fooFive: { type: 'Relation', targetClass: '_User' },
|
|
fooSix: { type: 'String' },
|
|
fooSeven: { type: 'Object' },
|
|
fooEight: { type: 'String' },
|
|
fooNine: { type: 'String' },
|
|
fooTeen: { type: 'Number' },
|
|
fooEleven: { type: 'String' },
|
|
fooTwelve: { type: 'String' },
|
|
fooThirteen: { type: 'String' },
|
|
fooFourteen: { type: 'String' },
|
|
fooFifteen: { type: 'String' },
|
|
fooSixteen: { type: 'String' },
|
|
fooEighteen: { type: 'String' },
|
|
fooNineteen: { type: 'String' },
|
|
},
|
|
classLevelPermissions: { ...levelPermissions },
|
|
indexes: {
|
|
_id_: { _id: 1 },
|
|
},
|
|
};
|
|
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
done();
|
|
})
|
|
.catch(error => {
|
|
console.trace(error);
|
|
done();
|
|
fail('Error creating class: ' + JSON.stringify(error));
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can update class level permission', done => {
|
|
const newLevelPermissions = {
|
|
find: {},
|
|
get: { '*': true },
|
|
count: {},
|
|
create: { '*': true },
|
|
update: {},
|
|
delete: { '*': true },
|
|
addField: {},
|
|
protectedFields: { '*': [] },
|
|
};
|
|
config.database.loadSchema().then(schema => {
|
|
schema
|
|
.validateObject('NewClass', { foo: 2 })
|
|
.then(() => schema.reloadData())
|
|
.then(() =>
|
|
schema.updateClass(
|
|
'NewClass',
|
|
{},
|
|
newLevelPermissions,
|
|
{},
|
|
config.database
|
|
)
|
|
)
|
|
.then(actualSchema => {
|
|
expect(dd(actualSchema.classLevelPermissions, newLevelPermissions)).toEqual(undefined);
|
|
done();
|
|
})
|
|
.catch(error => {
|
|
console.trace(error);
|
|
done();
|
|
fail('Error creating class: ' + JSON.stringify(error));
|
|
});
|
|
});
|
|
});
|
|
|
|
it('will fail to create a class if that class was already created by an object', done => {
|
|
config.database.loadSchema().then(schema => {
|
|
schema
|
|
.validateObject('NewClass', { foo: 7 })
|
|
.then(() => schema.reloadData())
|
|
.then(() =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'String' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
|
expect(error.message).toEqual('Class NewClass already exists.');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('will resolve class creation races appropriately', done => {
|
|
// If two callers race to create the same schema, the response to the
|
|
// race loser should be the same as if they hadn't been racing.
|
|
config.database.loadSchema().then(schema => {
|
|
const p1 = schema
|
|
.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'String' },
|
|
})
|
|
.then(validateSchema)
|
|
.catch(validateError);
|
|
const p2 = schema
|
|
.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'String' },
|
|
})
|
|
.then(validateSchema)
|
|
.catch(validateError);
|
|
let schemaValidated = false;
|
|
function validateSchema(actualSchema) {
|
|
const expectedSchema = {
|
|
className: 'NewClass',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
foo: { type: 'String' },
|
|
},
|
|
classLevelPermissions: {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
},
|
|
};
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
schemaValidated = true;
|
|
}
|
|
let errorValidated = false;
|
|
function validateError(error) {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
|
expect(error.message).toEqual('Class NewClass already exists.');
|
|
errorValidated = true;
|
|
}
|
|
Promise.all([p1, p2]).then(() => {
|
|
expect(schemaValidated).toEqual(true);
|
|
expect(errorValidated).toEqual(true);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('refuses to create classes with invalid names', done => {
|
|
config.database.loadSchema().then(schema => {
|
|
schema
|
|
.addClassIfNotExists('_InvalidName', { foo: { type: 'String' } })
|
|
.catch(error => {
|
|
expect(error.message).toEqual(
|
|
'Invalid classname: _InvalidName, classnames can only have alphanumeric characters and _, and must start with an alpha character '
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with invalid names', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
'0InvalidName': { type: 'String' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME);
|
|
expect(error.message).toEqual('invalid field name: 0InvalidName');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to explicitly create the default fields for custom classes', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', { objectId: { type: 'String' } })
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(136);
|
|
expect(error.message).toEqual('field objectId cannot be added');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to explicitly create the default fields for non-custom classes', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('_Installation', {
|
|
localeIdentifier: { type: 'String' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(136);
|
|
expect(error.message).toEqual('field localeIdentifier cannot be added');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with invalid types', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 7 },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
|
|
expect(error.message).toEqual('invalid JSON');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with invalid pointer types', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'Pointer' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(135);
|
|
expect(error.message).toEqual('type Pointer needs a class name');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with invalid pointer target', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'Pointer', targetClass: 7 },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
|
|
expect(error.message).toEqual('invalid JSON');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with invalid Relation type', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'Relation', uselessKey: 7 },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(135);
|
|
expect(error.message).toEqual('type Relation needs a class name');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with invalid relation target', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'Relation', targetClass: 7 },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
|
|
expect(error.message).toEqual('invalid JSON');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with uncreatable pointer target class', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'Pointer', targetClass: 'not a valid class name' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
|
expect(error.message).toEqual(
|
|
'Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character '
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with uncreatable relation target class', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'Relation', targetClass: 'not a valid class name' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
|
expect(error.message).toEqual(
|
|
'Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character '
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add fields with unknown types', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
foo: { type: 'Unknown' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE);
|
|
expect(error.message).toEqual('invalid field type: Unknown');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to add CLP with incorrect find', done => {
|
|
const levelPermissions = {
|
|
find: { '*': false },
|
|
get: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': ['email'] },
|
|
};
|
|
config.database.loadSchema().then(schema => {
|
|
schema
|
|
.validateObject('NewClass', {})
|
|
.then(() => schema.reloadData())
|
|
.then(() =>
|
|
schema.updateClass(
|
|
'NewClass',
|
|
{},
|
|
levelPermissions,
|
|
{},
|
|
config.database
|
|
)
|
|
)
|
|
.then(done.fail)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('refuses to add CLP when incorrectly sending a string to protectedFields object value instead of an array', done => {
|
|
const levelPermissions = {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': 'email' },
|
|
};
|
|
config.database.loadSchema().then(schema => {
|
|
schema
|
|
.validateObject('NewClass', {})
|
|
.then(() => schema.reloadData())
|
|
.then(() =>
|
|
schema.updateClass(
|
|
'NewClass',
|
|
{},
|
|
levelPermissions,
|
|
{},
|
|
config.database
|
|
)
|
|
)
|
|
.then(done.fail)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('will create classes', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
aNumber: { type: 'Number' },
|
|
aString: { type: 'String' },
|
|
aBool: { type: 'Boolean' },
|
|
aDate: { type: 'Date' },
|
|
aObject: { type: 'Object' },
|
|
aArray: { type: 'Array' },
|
|
aGeoPoint: { type: 'GeoPoint' },
|
|
aFile: { type: 'File' },
|
|
aPointer: {
|
|
type: 'Pointer',
|
|
targetClass: 'ThisClassDoesNotExistYet',
|
|
},
|
|
aRelation: { type: 'Relation', targetClass: 'NewClass' },
|
|
aBytes: { type: 'Bytes' },
|
|
aPolygon: { type: 'Polygon' },
|
|
})
|
|
)
|
|
.then(actualSchema => {
|
|
const expectedSchema = {
|
|
className: 'NewClass',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
aString: { type: 'String' },
|
|
aNumber: { type: 'Number' },
|
|
aBool: { type: 'Boolean' },
|
|
aDate: { type: 'Date' },
|
|
aObject: { type: 'Object' },
|
|
aArray: { type: 'Array' },
|
|
aGeoPoint: { type: 'GeoPoint' },
|
|
aFile: { type: 'File' },
|
|
aPointer: {
|
|
type: 'Pointer',
|
|
targetClass: 'ThisClassDoesNotExistYet',
|
|
},
|
|
aRelation: { type: 'Relation', targetClass: 'NewClass' },
|
|
aBytes: { type: 'Bytes' },
|
|
aPolygon: { type: 'Polygon' },
|
|
},
|
|
classLevelPermissions: {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
},
|
|
};
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('creates the default fields for non-custom classes', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('_Installation', {
|
|
foo: { type: 'Number' },
|
|
})
|
|
)
|
|
.then(actualSchema => {
|
|
const expectedSchema = {
|
|
className: '_Installation',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
foo: { type: 'Number' },
|
|
installationId: { type: 'String' },
|
|
deviceToken: { type: 'String' },
|
|
channels: { type: 'Array' },
|
|
deviceType: { type: 'String' },
|
|
pushType: { type: 'String' },
|
|
GCMSenderId: { type: 'String' },
|
|
timeZone: { type: 'String' },
|
|
localeIdentifier: { type: 'String' },
|
|
badge: { type: 'Number' },
|
|
appVersion: { type: 'String' },
|
|
appName: { type: 'String' },
|
|
appIdentifier: { type: 'String' },
|
|
parseVersion: { type: 'String' },
|
|
},
|
|
classLevelPermissions: {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
},
|
|
};
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('creates non-custom classes which include relation field', done => {
|
|
config.database
|
|
.loadSchema()
|
|
//as `_Role` is always created by default, we only get it here
|
|
.then(schema => schema.getOneSchema('_Role'))
|
|
.then(actualSchema => {
|
|
const expectedSchema = {
|
|
className: '_Role',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
name: { type: 'String' },
|
|
users: { type: 'Relation', targetClass: '_User' },
|
|
roles: { type: 'Relation', targetClass: '_Role' },
|
|
},
|
|
classLevelPermissions: {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
},
|
|
};
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('creates non-custom classes which include pointer field', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => schema.addClassIfNotExists('_Session', {}))
|
|
.then(actualSchema => {
|
|
const expectedSchema = {
|
|
className: '_Session',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
restricted: { type: 'Boolean' },
|
|
user: { type: 'Pointer', targetClass: '_User' },
|
|
installationId: { type: 'String' },
|
|
sessionToken: { type: 'String' },
|
|
expiresAt: { type: 'Date' },
|
|
createdWith: { type: 'Object' },
|
|
ACL: { type: 'ACL' },
|
|
},
|
|
classLevelPermissions: {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
},
|
|
};
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to create two geopoints', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.addClassIfNotExists('NewClass', {
|
|
geo1: { type: 'GeoPoint' },
|
|
geo2: { type: 'GeoPoint' },
|
|
})
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE);
|
|
expect(error.message).toEqual(
|
|
'currently, only one GeoPoint field may exist in an object. Adding geo2 when geo1 already exists.'
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can check if a class exists', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
return schema
|
|
.addClassIfNotExists('NewClass', {})
|
|
.then(() => schema.reloadData({ clearCache: true }))
|
|
.then(() => {
|
|
schema
|
|
.hasClass('NewClass')
|
|
.then(hasClass => {
|
|
expect(hasClass).toEqual(true);
|
|
done();
|
|
})
|
|
.catch(fail);
|
|
|
|
schema
|
|
.hasClass('NonexistantClass')
|
|
.then(hasClass => {
|
|
expect(hasClass).toEqual(false);
|
|
done();
|
|
})
|
|
.catch(fail);
|
|
})
|
|
.catch(error => {
|
|
fail("Couldn't create class");
|
|
jfail(error);
|
|
});
|
|
})
|
|
.catch(() => fail("Couldn't load schema"));
|
|
});
|
|
|
|
it('refuses to delete fields from invalid class names', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => schema.deleteField('fieldName', 'invalid class name'))
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to delete invalid fields', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema =>
|
|
schema.deleteField('invalid field name', 'ValidClassName')
|
|
)
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to delete the default fields', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => schema.deleteField('installationId', '_Installation'))
|
|
.catch(error => {
|
|
expect(error.code).toEqual(136);
|
|
expect(error.message).toEqual('field installationId cannot be changed');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to delete fields from nonexistant classes', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => schema.deleteField('field', 'NoClass'))
|
|
.catch(error => {
|
|
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
|
expect(error.message).toEqual('Class NoClass does not exist.');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('refuses to delete fields that dont exist', done => {
|
|
hasAllPODobject()
|
|
.save()
|
|
.then(() => config.database.loadSchema())
|
|
.then(schema => schema.deleteField('missingField', 'HasAllPOD'))
|
|
.catch(error => {
|
|
expect(error.code).toEqual(255);
|
|
expect(error.message).toEqual(
|
|
'Field missingField does not exist, cannot delete.'
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('drops related collection when deleting relation field', done => {
|
|
const obj1 = hasAllPODobject();
|
|
obj1
|
|
.save()
|
|
.then(savedObj1 => {
|
|
const obj2 = new Parse.Object('HasPointersAndRelations');
|
|
obj2.set('aPointer', savedObj1);
|
|
const relation = obj2.relation('aRelation');
|
|
relation.add(obj1);
|
|
return obj2.save();
|
|
})
|
|
.then(() =>
|
|
config.database.collectionExists(
|
|
'_Join:aRelation:HasPointersAndRelations'
|
|
)
|
|
)
|
|
.then(exists => {
|
|
if (!exists) {
|
|
fail('Relation collection ' + 'should exist after save.');
|
|
}
|
|
})
|
|
.then(() => config.database.loadSchema())
|
|
.then(schema =>
|
|
schema.deleteField(
|
|
'aRelation',
|
|
'HasPointersAndRelations',
|
|
config.database
|
|
)
|
|
)
|
|
.then(() =>
|
|
config.database.collectionExists(
|
|
'_Join:aRelation:HasPointersAndRelations'
|
|
)
|
|
)
|
|
.then(
|
|
exists => {
|
|
if (exists) {
|
|
fail(
|
|
'Relation collection should not exist after deleting relation field.'
|
|
);
|
|
}
|
|
done();
|
|
},
|
|
error => {
|
|
jfail(error);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can delete relation field when related _Join collection not exist', done => {
|
|
config.database.loadSchema().then(schema => {
|
|
schema
|
|
.addClassIfNotExists('NewClass', {
|
|
relationField: { type: 'Relation', targetClass: '_User' },
|
|
})
|
|
.then(actualSchema => {
|
|
const expectedSchema = {
|
|
className: 'NewClass',
|
|
fields: {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
relationField: { type: 'Relation', targetClass: '_User' },
|
|
},
|
|
classLevelPermissions: {
|
|
find: { '*': true },
|
|
get: { '*': true },
|
|
count: { '*': true },
|
|
create: { '*': true },
|
|
update: { '*': true },
|
|
delete: { '*': true },
|
|
addField: { '*': true },
|
|
protectedFields: { '*': [] },
|
|
},
|
|
};
|
|
expect(dd(actualSchema, expectedSchema)).toEqual(undefined);
|
|
})
|
|
.then(() =>
|
|
config.database.collectionExists('_Join:relationField:NewClass')
|
|
)
|
|
.then(exist => {
|
|
on_db(
|
|
'postgres',
|
|
() => {
|
|
// We create the table when creating the column
|
|
expect(exist).toEqual(true);
|
|
},
|
|
() => {
|
|
expect(exist).toEqual(false);
|
|
}
|
|
);
|
|
})
|
|
.then(() =>
|
|
schema.deleteField('relationField', 'NewClass', config.database)
|
|
)
|
|
.then(() => schema.reloadData())
|
|
.then(() => {
|
|
const expectedSchema = {
|
|
objectId: { type: 'String' },
|
|
updatedAt: { type: 'Date' },
|
|
createdAt: { type: 'Date' },
|
|
ACL: { type: 'ACL' },
|
|
};
|
|
expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual(
|
|
undefined
|
|
);
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
});
|
|
|
|
it('can delete string fields and resave as number field', done => {
|
|
Parse.Object.disableSingleInstance();
|
|
const obj1 = hasAllPODobject();
|
|
const obj2 = hasAllPODobject();
|
|
Parse.Object.saveAll([obj1, obj2])
|
|
.then(() => config.database.loadSchema())
|
|
.then(schema =>
|
|
schema.deleteField('aString', 'HasAllPOD', config.database)
|
|
)
|
|
.then(() => new Parse.Query('HasAllPOD').get(obj1.id))
|
|
.then(obj1Reloaded => {
|
|
expect(obj1Reloaded.get('aString')).toEqual(undefined);
|
|
obj1Reloaded.set('aString', ['not a string', 'this time']);
|
|
obj1Reloaded
|
|
.save()
|
|
.then(obj1reloadedAgain => {
|
|
expect(obj1reloadedAgain.get('aString')).toEqual([
|
|
'not a string',
|
|
'this time',
|
|
]);
|
|
return new Parse.Query('HasAllPOD').get(obj2.id);
|
|
})
|
|
.then(obj2reloaded => {
|
|
expect(obj2reloaded.get('aString')).toEqual(undefined);
|
|
done();
|
|
});
|
|
})
|
|
.catch(error => {
|
|
jfail(error);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can delete pointer fields and resave as string', done => {
|
|
Parse.Object.disableSingleInstance();
|
|
const obj1 = new Parse.Object('NewClass');
|
|
obj1
|
|
.save()
|
|
.then(() => {
|
|
obj1.set('aPointer', obj1);
|
|
return obj1.save();
|
|
})
|
|
.then(obj1 => {
|
|
expect(obj1.get('aPointer').id).toEqual(obj1.id);
|
|
})
|
|
.then(() => config.database.loadSchema())
|
|
.then(schema =>
|
|
schema.deleteField('aPointer', 'NewClass', config.database)
|
|
)
|
|
.then(() => new Parse.Query('NewClass').get(obj1.id))
|
|
.then(obj1 => {
|
|
expect(obj1.get('aPointer')).toEqual(undefined);
|
|
obj1.set('aPointer', 'Now a string');
|
|
return obj1.save();
|
|
})
|
|
.then(obj1 => {
|
|
expect(obj1.get('aPointer')).toEqual('Now a string');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can merge schemas', done => {
|
|
expect(
|
|
SchemaController.buildMergedSchemaObject(
|
|
{
|
|
_id: 'SomeClass',
|
|
someType: { type: 'Number' },
|
|
},
|
|
{
|
|
newType: { type: 'Number' },
|
|
}
|
|
)
|
|
).toEqual({
|
|
someType: { type: 'Number' },
|
|
newType: { type: 'Number' },
|
|
});
|
|
done();
|
|
});
|
|
|
|
it('can merge deletions', done => {
|
|
expect(
|
|
SchemaController.buildMergedSchemaObject(
|
|
{
|
|
_id: 'SomeClass',
|
|
someType: { type: 'Number' },
|
|
outDatedType: { type: 'String' },
|
|
},
|
|
{
|
|
newType: { type: 'GeoPoint' },
|
|
outDatedType: { __op: 'Delete' },
|
|
}
|
|
)
|
|
).toEqual({
|
|
someType: { type: 'Number' },
|
|
newType: { type: 'GeoPoint' },
|
|
});
|
|
done();
|
|
});
|
|
|
|
it('ignore default field when merge with system class', done => {
|
|
expect(
|
|
SchemaController.buildMergedSchemaObject(
|
|
{
|
|
_id: '_User',
|
|
username: { type: 'String' },
|
|
password: { type: 'String' },
|
|
email: { type: 'String' },
|
|
emailVerified: { type: 'Boolean' },
|
|
},
|
|
{
|
|
emailVerified: { type: 'String' },
|
|
customField: { type: 'String' },
|
|
}
|
|
)
|
|
).toEqual({
|
|
customField: { type: 'String' },
|
|
});
|
|
done();
|
|
});
|
|
|
|
it('yields a proper schema mismatch error (#2661)', done => {
|
|
const anObject = new Parse.Object('AnObject');
|
|
const anotherObject = new Parse.Object('AnotherObject');
|
|
const someObject = new Parse.Object('SomeObject');
|
|
Parse.Object.saveAll([anObject, anotherObject, someObject])
|
|
.then(() => {
|
|
anObject.set('pointer', anotherObject);
|
|
return anObject.save();
|
|
})
|
|
.then(() => {
|
|
anObject.set('pointer', someObject);
|
|
return anObject.save();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('shoud not save correctly');
|
|
done();
|
|
},
|
|
err => {
|
|
expect(err instanceof Parse.Error).toBeTruthy();
|
|
expect(err.message).toEqual(
|
|
'schema mismatch for AnObject.pointer; expected Pointer<AnotherObject> but got Pointer<SomeObject>'
|
|
);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('yields a proper schema mismatch error bis (#2661)', done => {
|
|
const anObject = new Parse.Object('AnObject');
|
|
const someObject = new Parse.Object('SomeObject');
|
|
Parse.Object.saveAll([anObject, someObject])
|
|
.then(() => {
|
|
anObject.set('number', 1);
|
|
return anObject.save();
|
|
})
|
|
.then(() => {
|
|
anObject.set('number', someObject);
|
|
return anObject.save();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('shoud not save correctly');
|
|
done();
|
|
},
|
|
err => {
|
|
expect(err instanceof Parse.Error).toBeTruthy();
|
|
expect(err.message).toEqual(
|
|
'schema mismatch for AnObject.number; expected Number but got Pointer<SomeObject>'
|
|
);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('yields a proper schema mismatch error ter (#2661)', done => {
|
|
const anObject = new Parse.Object('AnObject');
|
|
const someObject = new Parse.Object('SomeObject');
|
|
Parse.Object.saveAll([anObject, someObject])
|
|
.then(() => {
|
|
anObject.set('pointer', someObject);
|
|
return anObject.save();
|
|
})
|
|
.then(() => {
|
|
anObject.set('pointer', 1);
|
|
return anObject.save();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('shoud not save correctly');
|
|
done();
|
|
},
|
|
err => {
|
|
expect(err instanceof Parse.Error).toBeTruthy();
|
|
expect(err.message).toEqual(
|
|
'schema mismatch for AnObject.pointer; expected Pointer<SomeObject> but got Number'
|
|
);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('properly handles volatile _Schemas', done => {
|
|
function validateSchemaStructure(schema) {
|
|
expect(schema.hasOwnProperty('className')).toBe(true);
|
|
expect(schema.hasOwnProperty('fields')).toBe(true);
|
|
expect(schema.hasOwnProperty('classLevelPermissions')).toBe(true);
|
|
}
|
|
function validateSchemaDataStructure(schemaData) {
|
|
Object.keys(schemaData).forEach(className => {
|
|
const schema = schemaData[className];
|
|
// Hooks has className...
|
|
if (className != '_Hooks') {
|
|
expect(schema.hasOwnProperty('className')).toBe(false);
|
|
}
|
|
expect(schema.hasOwnProperty('fields')).toBe(false);
|
|
expect(schema.hasOwnProperty('classLevelPermissions')).toBe(false);
|
|
});
|
|
}
|
|
let schema;
|
|
config.database
|
|
.loadSchema()
|
|
.then(s => {
|
|
schema = s;
|
|
return schema.getOneSchema('_User', false);
|
|
})
|
|
.then(userSchema => {
|
|
validateSchemaStructure(userSchema);
|
|
validateSchemaDataStructure(schema.schemaData);
|
|
return schema.getOneSchema('_PushStatus', true);
|
|
})
|
|
.then(pushStatusSchema => {
|
|
validateSchemaStructure(pushStatusSchema);
|
|
validateSchemaDataStructure(schema.schemaData);
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('setAllClasses return classes if cache fails', async () => {
|
|
const schema = await config.database.loadSchema();
|
|
|
|
spyOn(schema._cache, 'setAllClasses').and.callFake(() =>
|
|
Promise.reject('Oops!')
|
|
);
|
|
const errorSpy = spyOn(console, 'error').and.callFake(() => {});
|
|
const allSchema = await schema.setAllClasses();
|
|
|
|
expect(allSchema).toBeDefined();
|
|
expect(errorSpy).toHaveBeenCalledWith(
|
|
'Error saving schema to cache:',
|
|
'Oops!'
|
|
);
|
|
});
|
|
|
|
it('should not throw on null field types', async () => {
|
|
const schema = await config.database.loadSchema();
|
|
const result = await schema.enforceFieldExists(
|
|
'NewClass',
|
|
'fieldName',
|
|
null
|
|
);
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('ensureFields should throw when schema is not set', async () => {
|
|
const schema = await config.database.loadSchema();
|
|
try {
|
|
schema.ensureFields([
|
|
{
|
|
className: 'NewClass',
|
|
fieldName: 'fieldName',
|
|
type: 'String',
|
|
},
|
|
]);
|
|
} catch (e) {
|
|
expect(e.message).toBe('Could not add field fieldName');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Class Level Permissions for requiredAuth', () => {
|
|
beforeEach(() => {
|
|
config = Config.get('test');
|
|
});
|
|
|
|
function createUser() {
|
|
const user = new Parse.User();
|
|
user.set('username', 'hello');
|
|
user.set('password', 'world');
|
|
return user.signUp(null);
|
|
}
|
|
|
|
it('required auth test find', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
find: {
|
|
requiresAuthentication: true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Class permissions should have rejected this query.');
|
|
done();
|
|
},
|
|
e => {
|
|
expect(e.message).toEqual(
|
|
'Permission denied, user needs to be authenticated.'
|
|
);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('required auth test find authenticated', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
find: {
|
|
requiresAuthentication: true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
return createUser();
|
|
})
|
|
.then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find();
|
|
})
|
|
.then(
|
|
results => {
|
|
expect(results.length).toEqual(0);
|
|
done();
|
|
},
|
|
e => {
|
|
console.error(e);
|
|
fail('Should not have failed');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('required auth should allow create authenticated', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
create: {
|
|
requiresAuthentication: true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
return createUser();
|
|
})
|
|
.then(() => {
|
|
const stuff = new Parse.Object('Stuff');
|
|
stuff.set('foo', 'bar');
|
|
return stuff.save();
|
|
})
|
|
.then(
|
|
() => {
|
|
done();
|
|
},
|
|
e => {
|
|
console.error(e);
|
|
fail('Should not have failed');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('required auth should reject create when not authenticated', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
create: {
|
|
requiresAuthentication: true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
const stuff = new Parse.Object('Stuff');
|
|
stuff.set('foo', 'bar');
|
|
return stuff.save();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Class permissions should have rejected this query.');
|
|
done();
|
|
},
|
|
e => {
|
|
expect(e.message).toEqual(
|
|
'Permission denied, user needs to be authenticated.'
|
|
);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('required auth test create/get/update/delete authenticated', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
create: {
|
|
requiresAuthentication: true,
|
|
},
|
|
get: {
|
|
requiresAuthentication: true,
|
|
},
|
|
delete: {
|
|
requiresAuthentication: true,
|
|
},
|
|
update: {
|
|
requiresAuthentication: true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
return createUser();
|
|
})
|
|
.then(() => {
|
|
const stuff = new Parse.Object('Stuff');
|
|
stuff.set('foo', 'bar');
|
|
return stuff.save().then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.get(stuff.id);
|
|
});
|
|
})
|
|
.then(gotStuff => {
|
|
return gotStuff.save({ foo: 'baz' }).then(() => {
|
|
return gotStuff.destroy();
|
|
});
|
|
})
|
|
.then(
|
|
() => {
|
|
done();
|
|
},
|
|
e => {
|
|
console.error(e);
|
|
fail('Should not have failed');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('required auth test create/get/update/delete not authenitcated', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
get: {
|
|
requiresAuthentication: true,
|
|
},
|
|
delete: {
|
|
requiresAuthentication: true,
|
|
},
|
|
update: {
|
|
requiresAuthentication: true,
|
|
},
|
|
create: {
|
|
'*': true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
const stuff = new Parse.Object('Stuff');
|
|
stuff.set('foo', 'bar');
|
|
return stuff.save().then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.get(stuff.id);
|
|
});
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Should not succeed!');
|
|
done();
|
|
},
|
|
e => {
|
|
expect(e.message).toEqual(
|
|
'Permission denied, user needs to be authenticated.'
|
|
);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('required auth test create/get/update/delete not authenitcated', done => {
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
find: {
|
|
requiresAuthentication: true,
|
|
},
|
|
delete: {
|
|
requiresAuthentication: true,
|
|
},
|
|
update: {
|
|
requiresAuthentication: true,
|
|
},
|
|
create: {
|
|
'*': true,
|
|
},
|
|
get: {
|
|
'*': true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
const stuff = new Parse.Object('Stuff');
|
|
stuff.set('foo', 'bar');
|
|
return stuff.save().then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query.get(stuff.id);
|
|
});
|
|
})
|
|
.then(result => {
|
|
expect(result.get('foo')).toEqual('bar');
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find();
|
|
})
|
|
.then(
|
|
() => {
|
|
fail('Should not succeed!');
|
|
done();
|
|
},
|
|
e => {
|
|
expect(e.message).toEqual(
|
|
'Permission denied, user needs to be authenticated.'
|
|
);
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('required auth test create/get/update/delete with roles (#3753)', done => {
|
|
let user;
|
|
config.database
|
|
.loadSchema()
|
|
.then(schema => {
|
|
// Just to create a valid class
|
|
return schema.validateObject('Stuff', { foo: 'bar' });
|
|
})
|
|
.then(schema => {
|
|
return schema.setPermissions('Stuff', {
|
|
find: {
|
|
requiresAuthentication: true,
|
|
'role:admin': true,
|
|
},
|
|
create: { 'role:admin': true },
|
|
update: { 'role:admin': true },
|
|
delete: { 'role:admin': true },
|
|
get: {
|
|
requiresAuthentication: true,
|
|
'role:admin': true,
|
|
},
|
|
});
|
|
})
|
|
.then(() => {
|
|
const stuff = new Parse.Object('Stuff');
|
|
stuff.set('foo', 'bar');
|
|
return stuff
|
|
.save(null, { useMasterKey: true })
|
|
.then(() => {
|
|
const query = new Parse.Query('Stuff');
|
|
return query
|
|
.get(stuff.id)
|
|
.then(
|
|
() => {
|
|
done.fail('should not succeed');
|
|
},
|
|
() => {
|
|
return new Parse.Query('Stuff').find();
|
|
}
|
|
)
|
|
.then(
|
|
() => {
|
|
done.fail('should not succeed');
|
|
},
|
|
() => {
|
|
return Promise.resolve();
|
|
}
|
|
);
|
|
})
|
|
.then(() => {
|
|
return Parse.User.signUp('user', 'password').then(signedUpUser => {
|
|
user = signedUpUser;
|
|
const query = new Parse.Query('Stuff');
|
|
return query.get(stuff.id, {
|
|
sessionToken: user.getSessionToken(),
|
|
});
|
|
});
|
|
});
|
|
})
|
|
.then(result => {
|
|
expect(result.get('foo')).toEqual('bar');
|
|
const query = new Parse.Query('Stuff');
|
|
return query.find({ sessionToken: user.getSessionToken() });
|
|
})
|
|
.then(
|
|
results => {
|
|
expect(results.length).toBe(1);
|
|
done();
|
|
},
|
|
e => {
|
|
console.error(e);
|
|
done.fail(e);
|
|
}
|
|
);
|
|
});
|
|
});
|