Parse.com: getting UserCannotBeAlteredWithoutSessionError - javascript

I have an Angular service that takes in a roleId and userId and assigns the user to that role and make a pointer in User to that role.
app.service('CRUD', function () {
this.addUserToRole = function (roleId, userId) {
// first we have to find the role we're adding to
var query = new Parse.Query(Parse.Role);
return query.get(roleId, {
success: function (role) {
// then add the user to it
var Database = Parse.Object.extend("User");
var query = new Parse.Query(Database);
console.log(role);
return query.get(userId, {
success: function (user) {
console.log(user);
role.getUsers().add(user);
role.save();
// now we need to tell the user that he has this role
console.log(user);
user.attributes.role.add(role);
user.save();
return user;
},
error: function (err) {
return err;
}
});
},
error: function (err) {
console.log(err);
}
});
}
});
I'm getting {"code":206,"error":"Parse::UserCannotBeAlteredWithoutSessionError"} on user.save();
After some research, I arrived at this website. He uses this code snippet as a JS SDK example:
Parse.Cloud.run('modifyUser', { username: 'userA' }, {
success: function(status) {
// the user was updated successfully
},
error: function(error) {
// error
}
});
and mentions something about a useMasterKey() function.
I'm still unsure how to fix this error.

Add
Parse.Cloud.useMasterKey();
at the beginning of your function.

Set it up as a background job. That is the code snip you found I think and a simpler far more secure means of fondling users and roles
https://parse.com/docs/cloud_code_guide#jobs

Related

Storing questions to an array of data

I've been trying to figure out how to add comments to an array of data in my local db. The idea I'm working on now is to add the array of comments to the array of data so it's easy to know which comment is for which set of data. When I try to do so using the following code, it won't work:
(This is in the userschema where I define the array that will hold the data)
test1: { type: array, required: false }
(Next I try to add the comment array with push but it won't work, I'm using test 0 as an example normally it will depend on what test you'd like to add your comments to. Test1 consists in turn of more arrays with the data in question to which I want to add the comments. Which is why I use user.test1[0])
user.test1[0].push(newComment);
(That does not work while the following does using splice)
user.test1.splice(1, 0, newComment)
For some reason it seems like a can't access the user.test1[0] but I have no idea why? Or should I use another technique when it comes to adding comments to a test?
app.updateTest1 = function(newComment1, newComment2, index) {
app.errorMsg = false; // Clear any error message
app.disabled = true; // Lock form while processing
// Check if username submitted is valid
var userObject = {}; // Create the user object to pass to function
userObject._id = app.currentUser; // Pass current user _id in order to edit
userObject.test1 = [$scope.newComment1, $scope.newComment2];
User.editUser(userObject).then(function(data) {
});
};
userFactory.editUser = function(id) {
return $http.put('/api/edit', id);
};
router.put('/edit', function(req, res) {
var editUser = req.body._id; // Assign _id from user to be editted to a variable
if (req.body.name) var newName = req.body.name; // Check if a change to name was requested
if (req.body.username) var newUsername = req.body.username; // Check if a change to username was requested
if (req.body.email) var newEmail = req.body.email; // Check if a change to e-mail was requested
if (req.body.permission) var newPermission = req.body.permission; // Check if a change to permission was requested
if (req.body.test1) {
var newTest1 = req.body.test1;
}
if (req.body.test2) {
var firstTest2 = req.body.test2;
var newTest2 = firstTest2.split(" ");
}
if (req.body.test3) {
var firstTest3 = req.body.test3;
var newTest3 = firstTest3.split(" ");
}
if (req.body.test4) {
var firstTest4 = req.body.test4;
var newTest4 = firstTest4.split(" ");
}
if (req.body.test5) {
var firstTest5 = req.body.test5;
var newTest5 = firstTest5.split(" ");
}
// Look for logged in user in database to check if have appropriate access
User.findOne({ username: req.decoded.username }, function(err, mainUser) {
if (err) {
// Create an e-mail object that contains the error. Set to automatically send it to myself for troubleshooting.
var email = {
from: 'MEAN Stack Staff, cruiserweights#zoho.com',
to: 'gugui3z24#gmail.com',
subject: 'Error Logged',
text: 'The following error has been reported in the MEAN Stack Application: ' + err,
html: 'The following error has been reported in the MEAN Stack Application:<br><br>' + err
};
// Function to send e-mail to myself
client.sendMail(email, function(err, info) {
if (err) {
console.log(err); // If error with sending e-mail, log to console/terminal
} else {
console.log(info); // Log success message to console if sent
console.log(user.email); // Display e-mail that it was sent to
}
});
res.json({ success: false, message: 'Something went wrong. This error has been logged and will be addressed by our staff. We apologize for this inconvenience!' });
} else {
// Check if logged in user is found in database
if (!mainUser) {
res.json({ success: false, message: "no user found" }); // Return error
} else {
// Check if a change to name was requested
if (newName) {
// Check if person making changes has appropriate access
if (mainUser.permission === 'admin' || mainUser.permission === 'moderator') {
// Look for user in database
User.findOne({ _id: editUser }, function(err, user) {
if (err) {
// Create an e-mail object that contains the error. Set to automatically send it to myself for troubleshooting.
var email = {
from: 'MEAN Stack Staff, cruiserweights#zoho.com',
to: 'gugui3z24#gmail.com',
subject: 'Error Logged',
text: 'The following error has been reported in the MEAN Stack Application: ' + err,
html: 'The following error has been reported in the MEAN Stack Application:<br><br>' + err
};
// Function to send e-mail to myself
client.sendMail(email, function(err, info) {
if (err) {
console.log(err); // If error with sending e-mail, log to console/terminal
} else {
console.log(info); // Log success message to console if sent
console.log(user.email); // Display e-mail that it was sent to
}
});
res.json({ success: false, message: 'Something went wrong. This error has been logged and will be addressed by our staff. We apologize for this inconvenience!' });
} else {
// Check if user is in database
if (!user) {
res.json({ success: false, message: 'No user found' }); // Return error
} else {
user.name = newName; // Assign new name to user in database
// Save changes
user.save(function(err) {
if (err) {
console.log(err); // Log any errors to the console
} else {
res.json({ success: true, message: 'Name has been updated!' }); // Return success message
}
});
}
}
});
} else {
res.json({ success: false, message: 'Insufficient Permissions' }); // Return error
}
}
if (newTest1) {
// Check if person making changes has appropriate access
if (mainUser.permission === 'admin') {
// Look for user in database
User.findOne({ _id: editUser }, function(err, user) {
if (err) {
res.json({ success: false, message: 'Something went wrong. This error has been logged and will be addressed by our staff. We apologize for this inconvenience!' });
} else {
// Check if user is in database
if (!user) {
res.json({ success: false, message: 'No user found' }); // Return error
} else {
-> (this is where i think the problem is) if (Array.isArray(newTest1)) {
var index = newTest1[2];
-> this doesn't work user.test1[0].push(newTest1);
//user.test1.splice(index, 0, newTest1)
} else {
var testet1 = newTest1.split(" ");
user.test1.push(testet1); // Assign new name to user in database
}
// Save changes
user.save(function(err) {
if (err) {
console.log(err); // Log any errors to the console
} else {
res.json({ success: true, message: 'Name has been updated!' }); // Return success message
}
});
}
}
});
} else {
res.json({ success: false, message: 'Insufficient Permissions' }); // Return error
}
}
You need to make sure user.test1[0] value exists before manipulating it. So if your user.test1
var user = {};
user.test = [];
var arrayToPush = [1,2,3];
try {
user.test[0].push(arrayToPush)
} catch (err) {
document.getElementById('out').innerHTML += err
}
var user2 ={}
user2.test=[];
try {
user2.test.push(arrayToPush)
user2.test.push(arrayToPush)
} catch (err) {
document.getElementById('out2').innerHTML += err
}
document.getElementById('out2').innerHTML += user2.test[0]
<div id="out"></div>
<br>
<div id="out2"></div>
array has no values at all you can't push to it anything.

How to trigger Parse.Cloud.afterSave on registers

I am looking for a way to trigger a Parse Cloud Job when a user register in my platform. This Job will set his role. Is it possible? I have try with this code but it is never triggered
Parse.Cloud.afterSave(Parse.User, function(request) {
Parse.Cloud.useMasterKey();
console.log('launch cloud request');
if (request.master === false) {
console.log('not mastered');
var query = new Parse.Query(Parse.Role);
query.equalTo('name', 'default');
query.first({
success: (default) => {
var defaultRelation = default.relation('users');
defaultRelation.add(request.object);
default.save();
},
error: (err) => console.error(err)
});
}
});
I think Parse.Cloud.useMasterKey() is deprecated at this time,
You can do that
Parse.Cloud.afterSave(Parse.User, function(request) {
console.log("Parse.Cloud.afterSave: ");
request.log.info("Parse.Cloud.afterSave: "); // For back4app user
});

Retrieve customer email from ID contained in webhook in Parse.com

I have an App using Parse.com as a backend and an external site that acts as my payment gateway. Upon receiving the customer/subscription webhook data from Stripe I wish to lookup the users email so I can then run a Cloud Code function and change their user status to 'paid'
My webhook receiver is:
Parse.Cloud.define("update_user", function(request, response) {
var data = request.params["data"]
var customer = data.object.customer;
response.success'Working' + request);
});
And I am able to get an email back from stripe from the customer ID using:
Parse.Cloud.define("pay", function(request, response) {
Stripe.initialize(STRIPE_SECRET_KEY);
console.log(JSON.stringify(request.params));
Stripe.Customers.retrieve(
customerId, {
success:function(results) {
console.log(results["email"]);
// alert(results["email"]);
response.success(results);
},
error:function(error) {
response.error("Error:" +error);
}
}
);
});
I need help turning this into a complete function that is run on receipt of every webhook from Stripe. I am also struggling with options for fallback if this does not work for whatever reason.
EDIT
Taking parts of the first answer and I now have:
Parse.Cloud.define("update_user", function(request, response) {
Stripe.initialize(STRIPE_SECRET_KEY);
var data = request.params["data"]
var customerId = data.object.customer;
get_stripe_customer(customerId, 100).then(function(stripeResponse) {
response.success(stripeResponse);
}, function(error) {
response.error(error);
});
});
function get_stripe_customer (customerId) {
Stripe.initialize(STRIPE_SECRET_KEY);
return Stripe.Customers.retrieve(
customerId, {
success:function(results) {
console.log(results["email"]);
},
error:function(error) {
}
}
);
};
My knowledge is really falling down on the Promise side of things and also the callback (success:, error, request response) etc further reading would be appreciated.
This is now working
Out of interest I did this:
Parse.Cloud.define("update_user", function(request, response) {
var data = request.params["data"]
var customerId = data.object.customer;
get_stripe_customer(customerId, 100).then(function(stripeResponse) {
return set_user_status(username, stripeResponse);
}).then(function(username) {
response.success(username);
}, function(error) {
response.error(error);
});
});
function get_stripe_customer (customerId) {
Stripe.initialize(STRIPE_SECRET_KEY);
return Stripe.Customers.retrieve(
customerId, {
success:function(results) {
// console.log(results["email"]);
},
error:function(error) {
}
}
);
};
function set_user_status(stripeResponse) {
Parse.Cloud.useMasterKey();
var emailquery = new Parse.Query(Parse.User);
emailquery.equalTo("username", stripeResponse['email']); // find all the women
return emailquery.first({
success: function(results) {
alert('running set_user_status success');
var user = results;
user.set("tier", "paid");
user.save();
},
error:function(error) {
console.log('error finding user');
}
});
};
open to improvements...
EDIT - I (#danh) cleaned it up a bit. A few notes:
used promises throughout. much easier to read and handle errors
get_stripe_customer requires only one param (that 100 was my idea to charge $100)
set_user_status appears to need only user email as param, which apparently is in the stripeResponse
set_user_status returns a promise to save the user. that will be fulfilled with the user object, not the username
be sure you're clear on how to identify the user. stripe apparently provides email address, but in your user query (in set_user_status) you compare email to "username". some systems set username == email. make sure yours does or change that query.
Parse.Cloud.define("update_user", function(request, response) {
var data = request.params["data"]
var customerId = data.object.customer;
get_stripe_customer(customerId).then(function(stripeResponse) {
var email = stripeResponse.email;
return set_user_status(email);
}).then(function(user) {
response.success(user);
}, function(error) {
response.error(error);
});
});
function get_stripe_customer(customerId) {
Stripe.initialize(STRIPE_SECRET_KEY);
return Stripe.Customers.retrieve(customerId).then(function(results) {
// console.log(results["email"]);
return results;
});
};
function set_user_status(email) {
Parse.Cloud.useMasterKey();
var emailquery = new Parse.Query(Parse.User);
emailquery.equalTo("username", email); // find all the women
return emailquery.first().then(function(user) {
user.set("tier", "paid");
return user.save();
}, function(error) {
console.log('error finding user ' + error.message);
return error;
});
}
Did a quick skim of the docs pertaining to stripe, and it looks like the steps are: (1) make a stripe REST-api call from your client side to get a token, (2) pass that token to a cloud function, (3) call stripe from the parse cloud to finish paying. I understand that you'd like to include a (4) fourth step wherein the transaction is recorded in the data for the paying user.
From the client (assuming a JS client):
var token = // we've retrieved this from Stripe's REST api
Parse.Cloud.run("pay", { stripeToken: token }).then(function(result) {
// success
}, function(error) {
// error
});
On the server:
Parse.Cloud.define("pay", function(request, response) {
var user = request.user;
var stripeToken = request.params.stripeToken;
payStripeWithToken(stripeToken, 100).then(function(stripeResponse) {
return updateUserWithStripeResult(user, stripeResponse);
}).then(function(user) {
response.success(user);
}, function(error) {
response.error(error);
});
});
Now we need only to build promise-returning functions called payStripeWithToken and updateUserWithStripeResult.
// return a promise to pay stripe per their api
function payStripeWithToken(stripeToken, dollarAmt) {
Stripe.initialize(STRIPE_SECRET_KEY); // didn't see this in the docs, borrowed from your code
return Stripe.Charges.create({
amount: dollarAmt * 10, // expressed in cents
currency: "usd",
card: stripeToken //the token id should be sent from the client
});
// caller does the success/error handling
}
// return a promise to update user with stripeResponse
function updateUserWithStripeResult(user, stripeResponse) {
var transactionId = // dig this out of the stripeResponse if you need it
user.set("paid", true);
user.set("transactionId", transactionId);
return user.save();
}

Problems with Queries in cloud code

Any ideas why this isn't working? It comes back with the success message but doesn't actually update anything.
I'm adding an FB user using Parse but since it doesn't have a func to add the username and email trying to do it this way. Any help much appreciated.
JS
Parse.Cloud.run("test", { objectId: "Q8XRUcL22N", name: "Rich", email: "rich#gmail.com"}, {
success: function(results) {
console.log(results);
},
error: function(error) {
console.log(error);
}
});
CLOUD CODE:
Parse.Cloud.define("test", function (request, response) {
Parse.Cloud.useMasterKey();
var uid;
var query = new Parse.Query("_User");
query.count({
success: function (results) {
uid = parseInt(results);
},
error: function () {
response.error("UID lookup failed");
}
});
var query = new Parse.Query(Parse.User);
query.equalTo("objectId", request.params.objectId);
query.first({
success: function (object) {
object.set("username", request.params.name);
object.set("email", request.params.email);
object.set("uid", uid);
var acl = new Parse.ACL();
acl.setPublicWriteAccess(false);
acl.setPublicReadAccess(false);
object.setACL(acl);
object.save();
response.success("Success Message");
},
error: function (error) {
response.error("Error Message");
}
});
});
Thanks
Calling success() or error() on the response halts whatever is underway at the time, including the save() on the user.
Also, it looks like you want to record in the user a count of users at the time the ACL is set. Getting the count must also be serialized with the other operations. (Also, please note that count is only good as an id "uid" to the extent the user count never goes down. What's wrong with the parse objects's id as an id?).
Most of the parse functions return promises, and using them is the only way to not go nuts trying to nest callbacks. So...
Parse.Cloud.define("test", function(request, response) {
Parse.Cloud.useMasterKey();
var uid;
var query = new Parse.Query(Parse.User);
query.count().then(function(count) {
uid = count;
return query.get(request.params.objectId);
}).then(function(object) {
object.set("username", request.params.name);
object.set("email", request.params.email);
object.set("uid", uid);
var acl = new Parse.ACL();
acl.setPublicWriteAccess(false);
acl.setPublicReadAccess(false);
object.setACL(acl);
return object.save();
}).then(function (object) {
response.success(object);
},function(error) {
response.error("Error Message");
});
});

Meteor insert uploaded into the console but not MongoDB

I've configured a FB graph call that would retrieve data from the API, however I'm having trouble inserting it into MongoDb. Right now if I run Photos.find().count(); in the browser it shows that there are photos, however if I run db.Photos.find().count(); in MongoDb it shows nothing. Also, if I run db.users.find(); in MongoDb it returns results from the FB user account, so MongoDb is talking to the API to some extent.
Any thoughts on what might be causing the issue?
Here is my code:
Client:
Template.test.events({
'click #btn-user-data': function(e) {
Meteor.call('getUserData', function(err, data) {
if(err) console.error(err);
});
}
});
Template.facebookphoto.helpers({
pictures: function () {
return Photos.find();
}
});
Server:
function Facebook(accessToken) {
this.fb = Meteor.require('fbgraph');
this.accessToken = accessToken;
this.fb.setAccessToken(this.accessToken);
this.options = {
timeout: 3000,
pool: {maxSockets: Infinity},
headers: {connection: "keep-alive"}
}
this.fb.setOptions(this.options);
}
Facebook.prototype.query = function(query, method) {
var self = this;
var method = (typeof method === 'undefined') ? 'get' : method;
var data = Meteor.sync(function(done) {
self.fb[method](query, function(err, res) {
done(null, res);
});
});
return data.result;
}
Facebook.prototype.getUserData = function() {
return this.query('me/photos');
}
Meteor.methods({
getUserData: function() {
var fb = new Facebook(Meteor.user().services.facebook.accessToken);
var data = fb.getUserData();
_.forEach(data.data, function(photo) {
if(Photos.findOne({id: photo.id})) return;
Photos.insert(photo, function(err) {
if(err) console.error(err);
});
});
}
});
Collection:
Photos = new Meteor.Collection('picture');
Thanks in advance!
Instead of db.Photos.find().count();, try db.picture.find().count();
Photos is just the name you gave to the JavaScript variable. The actual name of the collection in MongoDB is whatever you use when you initialized the Collection - in this case, picture.

Categories