diff --git a/spec/ParseRole.spec.js b/spec/ParseRole.spec.js index 47fed865fb..eef91ba8d7 100644 --- a/spec/ParseRole.spec.js +++ b/spec/ParseRole.spec.js @@ -188,7 +188,9 @@ describe('Parse Role testing', () => { // 1 call for the 1st layer of roles // 1 call for the 2nd layer - expect(getAllRolesSpy.calls.count()).toEqual(2); + expect(getAllRolesSpy.calls.count()).toEqual( + process.env.PARSE_SERVER_TEST_DB === 'postgres' ? 2 : 1 + ); done(); }) .catch(() => { diff --git a/src/Auth.js b/src/Auth.js index 0fe3b54460..1c52c4a424 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -5,6 +5,7 @@ import Deprecator from './Deprecator/Deprecator'; import { logger } from './logger'; import RestQuery from './RestQuery'; import RestWrite from './RestWrite'; +import MongoStorageAdapter from './Adapters/Storage/Mongo/MongoStorageAdapter'; // An Auth object tells you who is requesting something and whether // the master key was used. @@ -139,7 +140,6 @@ const getAuthForSessionToken = async function ({ limit: 1, include: 'user', }; - const RestQuery = require('./RestQuery'); const query = new RestQuery(config, master(config), '_Session', { sessionToken }, restOptions); results = (await query.execute()).results; } else { @@ -183,7 +183,6 @@ var getAuthForLegacySessionToken = function ({ config, sessionToken, installatio var restOptions = { limit: 1, }; - const RestQuery = require('./RestQuery'); var query = new RestQuery(config, master(config), '_User', { sessionToken }, restOptions); return query.execute().then(response => { var results = response.results; @@ -221,17 +220,113 @@ Auth.prototype.getRolesForUser = async function () { //Stack all Parse.Role const results = []; if (this.config) { - const restWhere = { - users: { - __type: 'Pointer', - className: '_User', - objectId: this.user.id, - }, - }; - const RestQuery = require('./RestQuery'); - await new RestQuery(this.config, master(this.config), '_Role', restWhere, {}).each(result => - results.push(result) - ); + if (this.config.database.adapter instanceof MongoStorageAdapter) { + const prefix = this.config.databaseAdapter._collectionPrefix || ''; + const result = await new RestQuery( + this.config, + master(this.config), + '_Join:users:_Role', + {}, + { + pipeline: [ + { + $match: { + relatedId: this.user.id, + }, + }, + { + $graphLookup: { + from: `${prefix}_Join:roles:_Role`, + startWith: '$owningId', + connectFromField: 'owningId', + connectToField: 'relatedId', + as: 'childRolePath', + }, + }, + { + $facet: { + directRoles: [ + { + $lookup: { + from: `${prefix}_Role`, + localField: 'owningId', + foreignField: '_id', + as: 'Roles', + }, + }, + { + $unwind: { + path: '$Roles', + }, + }, + { + $replaceRoot: { + newRoot: { + $ifNull: ['$Roles', { $literal: {} }], + }, + }, + }, + { + $project: { + name: 1, + }, + }, + ], + childRoles: [ + { + $lookup: { + from: `${prefix}_Role`, + localField: 'childRolePath.owningId', + foreignField: '_id', + as: 'Roles', + }, + }, + { + $unwind: { + path: '$Roles', + }, + }, + { + $replaceRoot: { + newRoot: { + $ifNull: ['$Roles', { $literal: {} }], + }, + }, + }, + { + $project: { + name: 1, + }, + }, + ], + }, + }, + ], + } + ).execute(); + const { directRoles, childRoles } = result.results[0] || { + directRoles: [], + childRoles: [], + }; + const roles = [...directRoles, ...childRoles]; + for (const role of roles) { + const roleName = `role:${role.name}`; + if (!results.includes(roleName)) { + results.push(role); + } + } + } else { + const restWhere = { + users: { + __type: 'Pointer', + className: '_User', + objectId: this.user.id, + }, + }; + await new RestQuery(this.config, master(this.config), '_Role', restWhere, {}).each(result => + results.push(result) + ); + } } else { await new Parse.Query(Parse.Role) .equalTo('users', this.user) @@ -257,25 +352,29 @@ Auth.prototype._loadRoles = async function () { this.userRoles = []; this.fetchedRoles = true; this.rolePromise = null; - this.cacheRoles(); return this.userRoles; } - const rolesMap = results.reduce( - (m, r) => { - m.names.push(r.name); - m.ids.push(r.objectId); - return m; - }, - { ids: [], names: [] } - ); + if (typeof results[0] === 'object') { + const rolesMap = results.reduce( + (m, r) => { + m.names.push(r.name); + m.ids.push(r.objectId); + return m; + }, + { ids: [], names: [] } + ); + + // run the recursive finding + const roleNames = await this._getAllRolesNamesForRoleIds(rolesMap.ids, rolesMap.names); + this.userRoles = roleNames.map(r => { + return 'role:' + r; + }); + } else { + this.userRoles = results; + } - // run the recursive finding - const roleNames = await this._getAllRolesNamesForRoleIds(rolesMap.ids, rolesMap.names); - this.userRoles = roleNames.map(r => { - return 'role:' + r; - }); this.fetchedRoles = true; this.rolePromise = null; this.cacheRoles(); @@ -322,7 +421,6 @@ Auth.prototype.getRolesByIds = async function (ins) { }; }); const restWhere = { roles: { $in: roles } }; - const RestQuery = require('./RestQuery'); await new RestQuery(this.config, master(this.config), '_Role', restWhere, {}).each(result => results.push(result) ); diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index f9c782db87..a8975cd54d 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -1293,7 +1293,7 @@ class DatabaseController { return this.adapter.distinct(className, schema, query, distinct); } } else if (pipeline) { - if (!classExists) { + if (!classExists && className.slice(0, 5) !== '_Join') { return []; } else { return this.adapter.aggregate(