Parse.com - secure sending data with javascript SDK - javascript

I'm building right now simple game with Angular JS and Parse.com cloud as my database.
My goal is in the and of the game, to store user score inside Parse cloud.
But how can i do this securly, when anyone can get access to my Parse keys, becouse they are visible in my js file, and simply recreate Parse Object with some fake data, and then store it in my database ?
ACL's it's not the point in this particular case - right now i just turn of write acl before save, to prevent users from changing they scores before save.
In my game i don't have any Parse Users - i want to all peaople play my game, without logging in.
What do you think about idea to make 'fake' user like in first answer in this post ( becouse Anonymous anonymous can't be create in JS parse SDK ), and then track the session and the user ?
Is it even helpful in my case ?
Maybe i should make some check in Cloude Code - like comparison Cookies or local storage data before saving in Parse ( it will make cheating in game harder but still possible ) ?
Below i present my whole service to show you what is all about:
angular.module('Parsedb', [])
.provider('Parsedbmanager', function() {
this.$get = function($q, $http) {
// new Parse constructor
var ParseHighScore = Parse.Object.extend('ParseHighScore');
// create new obj
var parseHighScore = new ParseHighScore();
this.parseInit = function() {
Parse.initialize('myKey', 'myKey');
};
this.setParsedb = function(newScore) {
// set val
parseHighScore.set('HighScore', newScore);
// save score to cloud
parseHighScore.save(null, {
success: function (parseHighScore) {
// protect from change saved obj
var acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(false);
parseHighScore.setACL(acl);
return parseHighScore.save();
},
error: function (parseHighScore, error) {
console.log('Failed to create new object, with error code: ' + error.message);
}
});
};
this.getParsedb = function() {
// need q to get this asynch
var deferred = $q.defer();
var query = new Parse.Query(ParseHighScore);
query.limit(5);
query.descending("HighScore");
query.find({
success: function(results) {
console.log("Successfully retrieved " + results.length + " scores.");
// resolve, if you have results
deferred.resolve(results);
},
error: function(error) {
deferred.reject(error.message);
}
});
return deferred.promise;
};
return this;
};
});

If you let the user to write to db then there will always be a situation where user can change data .. i think all you can do is, to abstract it from user

Related

Understanding JavaScript for TFS widget

I've been trying to modify the sample dashboard widget at this location
https://learn.microsoft.com/en-us/vsts/extend/develop/add-dashboard-widget?view=vsts#part-2-hello-world-with-vsts-rest-api
However, reluctantly have to admit I simply can't understand the structure required to extend it
Near the end, it uses "load: function" and returns the outputs of a REST API call, which I can consume however I want
However, I need to make more than one different REST call, and I simply cannot figure out how to get that info usable in my function
I modified the code so it starts like this:
VSS.require(["TFS/Dashboards/WidgetHelpers", "TFS/Work/RestClient","VSS/Service", "TFS/WorkItemTracking/RestClient" ],
I then created a handle for the other call I want to make like this:
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var queryResults = queryClient.getQuery(projectId, "Shared Queries/My Bugs");
However, I cannot consume the contents of queryResults - I know it's working up to a point as if I put in an invalid URL it will error as it knows it can't access anything there. If the URL is correct, no matter what I've tried - even stringify just to see what comes back - I get 'undefined' or something similar (it's definitely a valid JavaScript object)
The key seems to be right at the end when you have "load: function" except that only allows one thing to be returned? The reason I know this is if I change the function that it returns to be the one I've written rather than the one from the sample, it works fine - but the problem remains the same in that I can only process the results of one API call.
You can call more than one APIs, the code in that article is just the simple sample.
For Widget extension, you just need to return the status (e.g. Success()) in load function, so you can return status at the end of the function. For example:
var getQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to VSTS
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName: "<unknown>") ));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return true;
}, function (error) {
// Use the widget helper and return failure as Widget Status
console.log(error);
return false;
});
}
var getAnOhterQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to VSTS
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Bug")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName: "<unknown>") ));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return true;
}, function (error) {
// Use the widget helper and return failure as Widget Status
console.log(error);
return false;
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
var r1= getQueryInfo(widgetSettings);
var r2=getAnOhterQueryInfo(widgetSettings);
if(r1==true && r2==true){
return WidgetHelpers.WidgetStatusHelper.Success();
}else{
return WidgetHelpers.WidgetStatusHelper.Failure("failed, check error in console");
}
}

Cucumber Js callback issue? or feature issue?

I'd like to write a feature like this:
Scenario: new Singleton create
When a new, unmatchable identity is received
Then a new tin record should be created
And a new bronze record should be created
And a new gold record should be created
which would tie to steps like this:
defineSupportCode(function ({ Before, Given, Then, When }) {
var expect = require('chai').expect;
var chanceGenerator = require('./helpers/chanceGenerator')
var request = require('./helpers/requestGenerator')
let identMap;
// reset identMap before each scenario
Before(function () {
identMap = [];
});
// should generate a valid identity
// persist it in a local variable so it can be tested in later steps
// and persist to the db via public endpoint
When('a new, unmatchable identity is received', function (callback) {
identMap.push(chanceGenerator.identity());
request.pubPostIdentity(identMap[identMap.length-1], callback);
});
// use the local variable to retrieve Tin that was persisted
// validate the tin persisted all the props that it should have
Then('a new tin record should be created', function (callback) {
request.pubGetIdentity(identMap[identMap.length-1], callback);
// var self = this;
// request.pubGetIdentity(identMap[identMap.length-1], callback, () => {
// console.log('never gets here...');
// self.callback();
// callback();
// });
// request.pubGetIdentity(identMap[identMap.length-1], (callback) => {
// console.log('never gets here...');
// self.callback();
// callback();
// });
});
The issue that I'm having is that I can't do anything in the Then callback. That is where I'd like to be able to verify the response has the right data.
Here are relevant excerpts from the helper files:
var pubPostIdentity = function (ident, callback) {
console.log('pubIdentity');
var options = {
method: 'POST',
url: 'http://cucumber.utu.ai:4020/identity/' + ident.platform + '/' + ident.platformId,
headers: {
'X-Consumer-Custom-Id': ident.botId + '_' + ident.botId
},
body: JSON.stringify(ident)
};
console.log('ident: ', ident);
request(options, (err, response, body) => {
if (err) {
console.log('pubPostIdentity: ', err);
callback(err);
}
console.log('pubPostIdentity: ', response.statusCode);
callback();
});
}
// accept an identity and retrieve from staging via identity public endpoint
var pubGetIdentity = function (ident, callback) {
console.log('pubGetIdentity');
var options = {
method: 'GET',
url: 'http://cucumber.utu.ai:4020/identity/' + ident.platform + '/' + ident.platformId,
headers: {
'X-Consumer-Custom-Id': ident.botId + '_' + ident.botId
}
};
request(options, (err, response) => {
if (err) {
console.log('pubGetIdentity: ', err);
callback(err);
}
console.log('pubGetIdentity: ', response.body);
callback();
});
}
Something that we are considering as an option is to re-write the feature to fit a different step definition structure. If we re-wrote the feature like this:
Scenario: new Singleton create
When a new, unmatchable 'TIN_RECORD' is received
Then the Identity Record should be created successfully
When the Identity Record is retreived for 'tin'
Then a new 'tin' should be created
When the Identity Record is retreived for 'bronze'
Then a new 'bronze' should be created
When the Identity Record is retreived for 'gold'
Then a new 'gold' should be created
I believe it bypasses the instep callback issue we are wrestling with, but I really hate the breakdown of the feature. It makes the feature less readable and comprehensible to the business.
So... my question, the summary feature presented first, is it written wrong? Am I trying to get step definitions to do something that they shouldn't? Or is my lack of Js skills shining bright, and this should be very doable, I'm just screwing up the callbacks?
Firstly, I'd say your rewritten feature is wrong. You should never go back in the progression Given, When, Then. You are going back from the Then to the When, which is wrong.
Given is used for setting up preconditions. When is used for the actual test. Then is used for the assertions. Each scenario should be a single test, so should have very few When clauses. If you want, you can use Scenario Outlines to mix several very similar tests together.
In this case, is recommend to take it back to first principles and see if that works. Then build up slowly to get out working.
I suspect in this case that the problem is in some exception being thrown that isn't handled. You could try rewriting it to use promises instead, which will then be rejected on error. That gives better error reporting.

Parse send notification when object modified

I am trying to send a Push Notification through Parse Cloud Code when a certain object has been modified - "dirty"
I think I am almost there, but received an error because I believe am creating a new user instead of querying for one.
Parse.Cloud.beforeSave("Fact", function(request, response) {
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userID;
var query = new Parse.Query(Parse.Installation);
query.equalTo('user', targetUser);
Parse.Push.send({
where: query,
data: {
alert: "Your Fact was approved :)"
}
});
return;
}
}
response.success();
});
I found this post related to my problem. My question now is how to integrate the user query in my beforeSave block. Ideally I would create another function for the user query and place that in my beforeSave block.
**5/14 Update
I took #toddg's advice and fixed the before save. Here is a clearer picture of what I am trying to do and the new error.
A couple points (as #Subash noted in the comments) before I get into the code:
Parse.Push.send is an async operation, so you'll want to ensure you call response.success() after your push send completes. I'm going to deal with this using Promises, as I think they are more flexible than callbacks. If you're not familiar, read about them here
The return in your if statement will likely prevent the response.success() from being called.
Here's my recommended way of doing it:
Parse.Cloud.beforeSave("Fact", function(request, response) {
// Keep track of whether we need to send the push notification
var shouldPushBeSent = false;
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
shouldPushBeSent = true;
}
}
if (shouldPushBeSent) {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userId;
var query = new Parse.Query(Parse.Installation);
// We want to pass the User object to the query rather than the UserId
query.equalTo('user', targetUser);
Parse.Push.send({
where: query, // Set our Installation query
data: {
alert: "Your fact was approved"
}
}).then(function(){
// Now we know the push notification was successfully sent
response.success();
}, function(error){
// There was an error sending the push notification
response.error("We had an error sending push: " + error);
});
} else {
// We don't need to send the push notification.
response.success();
}
});
By the way, I'm assuming that you have a column on your Installation class that tracks which user is associated with each Installation.

Update Object or Create it if it doesn't exists?

Im digging though some cloud code to do some manipulation of data and save it as a new class.
I have a situation where I read a row from another class, do some math functions, then save the manipulated data to another class which is then read by our client.
The problem is that If the other object already exists in the new class, I just want to update it instead of creating a new one. I know in the parse documentation
it lists creating an object and updating but not really functionality to update if exists, and if not create.
Here is just some example code.. the out data is the data prepped to save for the new class. I can crate the new class object, but when I update some value that
should trigger a update instead of a create new is where things fall apart.
Please understand JS is not my first language so this might be hacked or completely going about it the wrong way, but I should stress I do not know the objectId of
the new class.
if(out.length > 0) {
var game = Parse.Object.extend("Gamers");
var query = new Parse.Query(game);
query.equalTo("playername", player); // using this to find the player since I dont have the objectid
query.find({
success: function(results) {
// Successfully retrieved the object.
if (results && results.length == "1") {
var playerObjectId = results[0].id
/// save only updated data to the local class ????
} else {
// no results, create a new local
console.log('no results')
// save as a new object
var gamers = new game();
gamers.set("somevalue", somevalue);
gamers.set("somevalue2", somevalue2);
gamers.save(null, {
success: function(gamers) {
// Execute any logic that should take place after the object is saved.
console.log('New object created with objectId: ' + gamers.id);
},
error: function(gamers, error) {
// Execute any logic that should take place if the save fails.
// error is a Parse.Error with an error code and message.
console.log('Failed to create new object, with error code: ' + error.message);
}
});
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
} else {
console.log('array is empty, something went wrong')
//this array is empty
}
A create or update function has three distinct bits: find, update, or possibly create. Lets build the three separately.
function findGamerWithName(name) {
var game = Parse.Object.extend("Gamers");
query.equalTo("playername", name);
return query.find();
}
function updateGamer(gamer, someValue, someValue2) {
gamer.set("somevalue", someValue);
gamer.set("somevalue2", someValue2);
return gamer.save();
}
function createGamer(someValue, someValue2) {
var Gamer = Parse.Object.extend("Gamers");
var gamer = new Gamer();
return updateGamer(gamer, someValue, someValue2);
}
Now we can understand and test these separately (you should test them). And now, it's easy to write create or update logic...
function createOrUpdateGamer(name, someValue, someValue2) {
return findGamerWithName(name).then(function(gamer) {
return (gamer)? updateGamer(gamer, someValue, someValue2) : createGamer(someValue, someValue2);
});
}

Trying to write to User class parse, masterkey is not working

I want to update a field within the User class without being logged in as a user. From reading online and other responses people say I should use the 'masterkey' to do so. Here is my cloud code where I have added in the master key. The code is executed but when I go to my data browser the totalScore and predictions values are still the same and not updated to the new values.
Parse.initialize("key", "key");
Parse.Cloud.define("userUpdate", function(request, response) {
Parse.Cloud.useMasterKey();
var publicReadACL = new Parse.ACL();
publicReadACL.setPublicWriteAccess(true);
request.object.setACL(publicReadACL);
var User = Parse.Object.extend("User");
var query = new Parse.Query(User);
query.equalTo("username", request.params.username);
query.find({
success: function(user) {
user.set("totalScore", request.params.totalS);
user.set("totalPredictions", request.params.totalG);
user.save()
},
error: function() {
response.error("f");
}
});
});
Any help would be massively appreciated.
Hopefully you've figured this out by now, but if you haven't...I would first say check to make sure that you're passing the "totalScore" and "totalPredictions" as numbers. If you pass them as strings and Parse is expecting a Number, it won't update. And generally, I believe it's best practice to query the user class as follows:
var query = new Parse.Query(Parse.User);
query.get(user.objectId, {
success: function(userAgain) {
userAgain.set("totalScore", totalScore);
userAgain.save(null, {
error: function(userAgain, error) {
// This will error, since the Parse.User is not authenticated
}
});
}
});
Then of course you'd still need to include the master key stuff etc...

Categories