Live query CLP (#4387)

* Auth module refactoring in order to be reusable

* Ensure cache controller is properly forwarded from helpers

* Nits

* Adds support for static validation

* Adds support for CLP in Live query (no support for roles yet)

* Adds e2e test to validate liveQuery hooks is properly called

* Adds tests over LiveQueryController to ensure data is correctly transmitted

* nits

* Fixes for flow types

* Removes usage of Parse.Promise

* Use the Auth module for authentication and caches

* Cleaner implementation of getting auth

* Adds authCache that stores auth promises

* Proper testing of the caching

* nits
This commit is contained in:
Florent Vilmart
2018-10-17 17:53:49 -04:00
committed by GitHub
parent 17bd5c3adb
commit 7c81290252
12 changed files with 829 additions and 237 deletions

View File

@@ -1335,7 +1335,7 @@ class DatabaseController {
}
addPointerPermissions(
schema: any,
schema: SchemaController.SchemaController,
className: string,
operation: string,
query: any,
@@ -1343,10 +1343,10 @@ class DatabaseController {
) {
// Check if class has public permission for operation
// If the BaseCLP pass, let go through
if (schema.testBaseCLP(className, aclGroup, operation)) {
if (schema.testPermissionsForClassName(className, aclGroup, operation)) {
return query;
}
const perms = schema.schemaData[className].classLevelPermissions;
const perms = schema.getClassLevelPermissions(className);
const field =
['get', 'find'].indexOf(operation) > -1
? 'readUserFields'

View File

@@ -16,19 +16,37 @@ export class LiveQueryController {
this.liveQueryPublisher = new ParseCloudCodePublisher(config);
}
onAfterSave(className: string, currentObject: any, originalObject: any) {
onAfterSave(
className: string,
currentObject: any,
originalObject: any,
classLevelPermissions: ?any
) {
if (!this.hasLiveQuery(className)) {
return;
}
const req = this._makePublisherRequest(currentObject, originalObject);
const req = this._makePublisherRequest(
currentObject,
originalObject,
classLevelPermissions
);
this.liveQueryPublisher.onCloudCodeAfterSave(req);
}
onAfterDelete(className: string, currentObject: any, originalObject: any) {
onAfterDelete(
className: string,
currentObject: any,
originalObject: any,
classLevelPermissions: any
) {
if (!this.hasLiveQuery(className)) {
return;
}
const req = this._makePublisherRequest(currentObject, originalObject);
const req = this._makePublisherRequest(
currentObject,
originalObject,
classLevelPermissions
);
this.liveQueryPublisher.onCloudCodeAfterDelete(req);
}
@@ -36,13 +54,20 @@ export class LiveQueryController {
return this.classNames.has(className);
}
_makePublisherRequest(currentObject: any, originalObject: any): any {
_makePublisherRequest(
currentObject: any,
originalObject: any,
classLevelPermissions: ?any
): any {
const req = {
object: currentObject,
};
if (currentObject) {
req.original = originalObject;
}
if (classLevelPermissions) {
req.classLevelPermissions = classLevelPermissions;
}
return req;
}
}

View File

@@ -1122,18 +1122,28 @@ export default class SchemaController {
return Promise.resolve(this);
}
// Validates the base CLP for an operation
testBaseCLP(className: string, aclGroup: string[], operation: string) {
const classSchema = this.schemaData[className];
if (
!classSchema ||
!classSchema.classLevelPermissions ||
!classSchema.classLevelPermissions[operation]
) {
testPermissionsForClassName(
className: string,
aclGroup: string[],
operation: string
) {
return SchemaController.testPermissions(
this.getClassLevelPermissions(className),
aclGroup,
operation
);
}
// Tests that the class level permission let pass the operation for a given aclGroup
static testPermissions(
classPermissions: ?any,
aclGroup: string[],
operation: string
): boolean {
if (!classPermissions || !classPermissions[operation]) {
return true;
}
const perms = classSchema.classLevelPermissions[operation];
// Handle the public scenario quickly
const perms = classPermissions[operation];
if (perms['*']) {
return true;
}
@@ -1149,21 +1159,22 @@ export default class SchemaController {
}
// Validates an operation passes class-level-permissions set in the schema
validatePermission(className: string, aclGroup: string[], operation: string) {
if (this.testBaseCLP(className, aclGroup, operation)) {
static validatePermission(
classPermissions: ?any,
className: string,
aclGroup: string[],
operation: string
) {
if (
SchemaController.testPermissions(classPermissions, aclGroup, operation)
) {
return Promise.resolve();
}
const classSchema = this.schemaData[className];
if (
!classSchema ||
!classSchema.classLevelPermissions ||
!classSchema.classLevelPermissions[operation]
) {
if (!classPermissions || !classPermissions[operation]) {
return true;
}
const classPerms = classSchema.classLevelPermissions;
const perms = classSchema.classLevelPermissions[operation];
const perms = classPermissions[operation];
// If only for authenticated users
// make sure we have an aclGroup
if (perms['requiresAuthentication']) {
@@ -1201,8 +1212,8 @@ export default class SchemaController {
// Process the readUserFields later
if (
Array.isArray(classPerms[permissionField]) &&
classPerms[permissionField].length > 0
Array.isArray(classPermissions[permissionField]) &&
classPermissions[permissionField].length > 0
) {
return Promise.resolve();
}
@@ -1212,6 +1223,23 @@ export default class SchemaController {
);
}
// Validates an operation passes class-level-permissions set in the schema
validatePermission(className: string, aclGroup: string[], operation: string) {
return SchemaController.validatePermission(
this.getClassLevelPermissions(className),
className,
aclGroup,
operation
);
}
getClassLevelPermissions(className: string): any {
return (
this.schemaData[className] &&
this.schemaData[className].classLevelPermissions
);
}
// Returns the expected type for a className+key combination
// or undefined if the schema is not set
getExpectedType(