Description
LiveQuery WebSocket server ignoresenableAnonymousUsers = false
setting and allows an anonymous user to connect and subscribe to queries even if sessionToken
is not passed.
The LiveQuery server should prevent clients from connecting if this setting is turned off, and make both servers work consistently. This also prevents the need of setting ACL on each document if the intent is only check for current logged-in users and thus reducing the servers overhead.
I also unchecked the GameScore class public read and write operations in CLP via the Parse Dashboard, so I expected the server to block read operations from unknown users. Then I read the LiveQuery docs and it does not take into account CLP. I think it could be interesting to have less granular security management in some scenarios, like this one.
Steps to reproduce
- Install latest version,
git clone https://github.com/ParsePlatform/parse-server-example.git
andnpm install
- Create a GameScore class with some fields, like [playerName, score, cheatMode], goto security (CLP) and uncheck Public Read and Write.
- Change the server index.js file
var api = new ParseServer({
databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
appId: process.env.APP_ID || 'myAppId',
masterKey: process.env.MASTER_KEY || 'masterKey', //Add your master key here. Keep it secret!
//clientKey: process.env.CLIENT_KEY || 'clientKey',
serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse',
enableAnonymousUsers : process.env.ENABLE_ANON_USERS || false,
allowClientClassCreation: process.env.CLIENT_CLASS_CREATION || false,
verbose : process.env.VERBOSE || 1,
liveQuery: {
classNames: ["GameScore"] // List of classes to support for query subscriptions
}
});
- Create an empty html file add this javascript script
Parse.initialize("myAppId");
Parse.serverURL = 'http://localhost:1337/parse';
console.log("Current user", Parse.User.current());
let query = new Parse.Query('GameScore');
query.find().then((data, err)=>{
console.log("Find = ",data, err);
});
let subscription = query.subscribe();
subscription.on('open', () => {
console.log("Connection Open");
});
Parse.LiveQuery.on('error', (error) => {
console.error("Connection Error", error);
});
Expected Results
The logs messages should be
Current User, null
Connection Error ...
The connection should be rejected and a console message should be Connection Error. The client should not receive additional messages from the ws server.
Actual Outcome
Instead you get in the browsers console the log message
Current User, null
Connection Open
As proof, open chrome dev tools -> network, filter by WS (websocket) connections and open the row with the parse name, then select the frames tab, you get data frames every time the subscribed data is changed. So to test this open Parse Dashboard and create, delete or edit documents in the GameScore collection.
Environment Setup
- Server
- parse-server version : 2.2.22
- Operating System: Windows 10.0.14393
- Hardware: Intel i7 64x, 8G ram
- Localhost with Node 6.7.0
- Database
- MongoDB version: 3.0.0
- Storage engine: { storage: { dbPath: "./db", journal: { enabled: true }, mmapv1: { smallFiles: true } } }
Logs/Trace
verbose: Current clients 0
verbose: Current subscriptions 0
verbose: REQUEST for [GET] /parse/classes/GameScore: {
"where": {
},
} method=GET, url=/parse/classes/GameScore, host=localhost:1337, connection=keep-alive, content-length=177, pragma=no-cache, cache-control=no-cache, origin=http://localhost:1337, user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36, content-type=text/plain, accept=*/*, referer=http://localhost:1337/public/test.html, accept-encoding=gzip, deflate, ,pt;q=0.8,en-US;q=0.6,en;q=0.4,
error: Error generating response. ParseError {
code: 119,
message: 'Permission denied for action find on class GameScore.' } code=119, message=Permission denied for action find on class GameScore.
verbose: Request: %j op=connect, applicationId=myAppId
info: Create new client: 1
verbose: Push Response : "{\"op\":\"connected\",\"clientId\":1}"
[object Object]
verbose: Request: %j op=subscribe, requestId=1, className=GameScore,
verbose: Push Response : "{\"op\":\"subscribed\",\"clientId\":1,\"requestId\":1}"
verbose: Create client 1 new subscription: 1
verbose: Current client number: 1