Skip to content

Commit 9bc1ecc

Browse files
authored
Merge branch 'alpha' into moumouls/fixLRU
2 parents 95ab9e3 + 528690b commit 9bc1ecc

File tree

9 files changed

+158
-22
lines changed

9 files changed

+158
-22
lines changed

changelogs/CHANGELOG_alpha.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# [5.3.0-alpha.16](https://github.com/parse-community/parse-server/compare/5.3.0-alpha.15...5.3.0-alpha.16) (2022-06-11)
2+
3+
4+
### Bug Fixes
5+
6+
* live query role cache does not clear when a user is added to a role ([#8026](https://github.com/parse-community/parse-server/issues/8026)) ([199dfc1](https://github.com/parse-community/parse-server/commit/199dfc17226d85a78ab85f24362cce740f4ada39))
7+
18
# [5.3.0-alpha.15](https://github.com/parse-community/parse-server/compare/5.3.0-alpha.14...5.3.0-alpha.15) (2022-06-05)
29

310

package-lock.json

Lines changed: 45 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "parse-server",
3-
"version": "5.3.0-alpha.15",
3+
"version": "5.3.0-alpha.16",
44
"description": "An express module providing a Parse-compatible API server",
55
"main": "lib/index.js",
66
"repository": {
@@ -20,9 +20,9 @@
2020
"license": "BSD-3-Clause",
2121
"dependencies": {
2222
"@graphql-yoga/node": "2.6.0",
23-
"@graphql-tools/merge": "8.2.11",
24-
"@graphql-tools/schema": "8.3.11",
25-
"@graphql-tools/utils": "8.6.10",
23+
"@graphql-tools/utils": "8.6.12",
24+
"@graphql-tools/merge": "8.2.13",
25+
"@graphql-tools/schema": "8.3.13",
2626
"@parse/fs-files-adapter": "1.2.2",
2727
"@parse/push-adapter": "4.1.2",
2828
"bcryptjs": "2.4.3",
@@ -38,7 +38,7 @@
3838
"graphql-relay": "0.10.0",
3939
"intersect": "1.0.1",
4040
"jsonwebtoken": "8.5.1",
41-
"jwks-rsa": "2.1.2",
41+
"jwks-rsa": "2.1.3",
4242
"ldapjs": "2.3.2",
4343
"lodash": "4.17.21",
4444
"lru-cache": "7.10.1",

spec/ParseLiveQuery.spec.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,50 @@ describe('ParseLiveQuery', function () {
836836
}
837837
});
838838

839+
it('LiveQuery should work with changing role', async () => {
840+
await reconfigureServer({
841+
liveQuery: {
842+
classNames: ['Chat'],
843+
},
844+
startLiveQueryServer: true,
845+
});
846+
const user = new Parse.User();
847+
user.setUsername('username');
848+
user.setPassword('password');
849+
await user.signUp();
850+
851+
const role = new Parse.Role('Test', new Parse.ACL(user));
852+
await role.save();
853+
854+
const chatQuery = new Parse.Query('Chat');
855+
const subscription = await chatQuery.subscribe();
856+
subscription.on('create', () => {
857+
fail('should not call create as user is not part of role.');
858+
});
859+
860+
const object = new Parse.Object('Chat');
861+
const acl = new Parse.ACL();
862+
acl.setRoleReadAccess(role, true);
863+
object.setACL(acl);
864+
object.set({ foo: 'bar' });
865+
await object.save(null, { useMasterKey: true });
866+
role.getUsers().add(user);
867+
await new Promise(resolve => setTimeout(resolve, 1000));
868+
await role.save();
869+
await new Promise(resolve => setTimeout(resolve, 1000));
870+
object.set('foo', 'yolo');
871+
await Promise.all([
872+
new Promise(resolve => {
873+
subscription.on('update', obj => {
874+
expect(obj.get('foo')).toBe('yolo');
875+
expect(obj.getACL().toJSON()).toEqual({ 'role:Test': { read: true } });
876+
resolve();
877+
});
878+
}),
879+
object.save(null, { useMasterKey: true }),
880+
]);
881+
});
882+
839883
it('liveQuery on Session class', async done => {
840884
await reconfigureServer({
841885
liveQuery: { classNames: [Parse.Session] },

src/Auth.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,15 @@ Auth.prototype.cacheRoles = function () {
230230
return true;
231231
};
232232

233+
Auth.prototype.clearRoleCache = function (sessionToken) {
234+
if (!this.cacheController) {
235+
return false;
236+
}
237+
this.cacheController.role.del(this.user.id);
238+
this.cacheController.user.del(sessionToken);
239+
return true;
240+
};
241+
233242
Auth.prototype.getRolesByIds = async function (ins) {
234243
const results = [];
235244
// Build an OR query across all parentRoles

src/Controllers/LiveQueryController.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ export class LiveQueryController {
5656
return false;
5757
}
5858

59+
clearCachedRoles(user: any) {
60+
if (!user) {
61+
return;
62+
}
63+
return this.liveQueryPublisher.onClearCachedRoles(user);
64+
}
65+
5966
_makePublisherRequest(currentObject: any, originalObject: any, classLevelPermissions: ?any): any {
6067
const req = {
6168
object: currentObject,

src/LiveQuery/ParseCloudCodePublisher.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ class ParseCloudCodePublisher {
1919
this._onCloudCodeMessage(Parse.applicationId + 'afterDelete', request);
2020
}
2121

22+
onClearCachedRoles(user: Parse.Object) {
23+
this.parsePublisher.publish(
24+
Parse.applicationId + 'clearCache',
25+
JSON.stringify({ userId: user.id })
26+
);
27+
}
28+
2229
// Request is the request object from cloud code functions. request.object is a ParseObject.
2330
_onCloudCodeMessage(type: string, request: any): void {
2431
logger.verbose(

src/LiveQuery/ParseLiveQueryServer.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class ParseLiveQueryServer {
7777
this.subscriber = ParsePubSub.createSubscriber(config);
7878
this.subscriber.subscribe(Parse.applicationId + 'afterSave');
7979
this.subscriber.subscribe(Parse.applicationId + 'afterDelete');
80+
this.subscriber.subscribe(Parse.applicationId + 'clearCache');
8081
// Register message handler for subscriber. When publisher get messages, it will publish message
8182
// to the subscribers and the handler will be called.
8283
this.subscriber.on('message', (channel, messageStr) => {
@@ -88,6 +89,10 @@ class ParseLiveQueryServer {
8889
logger.error('unable to parse message', messageStr, e);
8990
return;
9091
}
92+
if (channel === Parse.applicationId + 'clearCache') {
93+
this._clearCachedRoles(message.userId);
94+
return;
95+
}
9196
this._inflateParseObject(message);
9297
if (channel === Parse.applicationId + 'afterSave') {
9398
this._onAfterSave(message);
@@ -474,6 +479,32 @@ class ParseLiveQueryServer {
474479
return matchesQuery(parseObject, subscription.query);
475480
}
476481

482+
async _clearCachedRoles(userId: string) {
483+
try {
484+
const validTokens = await new Parse.Query(Parse.Session)
485+
.equalTo('user', Parse.User.createWithoutData(userId))
486+
.find({ useMasterKey: true });
487+
await Promise.all(
488+
validTokens.map(async token => {
489+
const sessionToken = token.get('sessionToken');
490+
const authPromise = this.authCache.get(sessionToken);
491+
if (!authPromise) {
492+
return;
493+
}
494+
const [auth1, auth2] = await Promise.all([
495+
authPromise,
496+
getAuthForSessionToken({ cacheController: this.cacheController, sessionToken }),
497+
]);
498+
auth1.auth?.clearRoleCache(sessionToken);
499+
auth2.auth?.clearRoleCache(sessionToken);
500+
this.authCache.del(sessionToken);
501+
})
502+
);
503+
} catch (e) {
504+
logger.verbose(`Could not clear role cache. ${e}`);
505+
}
506+
}
507+
477508
getAuthForSessionToken(sessionToken: ?string): Promise<{ auth: ?Auth, userId: ?string }> {
478509
if (!sessionToken) {
479510
return Promise.resolve({});
@@ -580,7 +611,6 @@ class ParseLiveQueryServer {
580611
if (!acl_has_roles) {
581612
return false;
582613
}
583-
584614
const roleNames = await auth.getUserRoles();
585615
// Finally, see if any of the user's roles allow them read access
586616
for (const role of roleNames) {

src/RestWrite.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,9 @@ RestWrite.prototype.runDatabaseOperation = function () {
13261326

13271327
if (this.className === '_Role') {
13281328
this.config.cacheController.role.clear();
1329+
if (this.config.liveQueryController) {
1330+
this.config.liveQueryController.clearCachedRoles(this.auth.user);
1331+
}
13291332
}
13301333

13311334
if (this.className === '_User' && this.query && this.auth.isUnauthenticated()) {

0 commit comments

Comments
 (0)