Calling Parse.Cloud.beforeSave() From custom web hooks - parse-platform

I started off cloning the cloud code example blog app. And in the controllers I have the basic REST crud operations. I tried adding the code here to my controller, as well as just directly in main.js:
Parse.Cloud.beforeSave(Parse.User, function(req, res)
{
for (var dirtyKey in req.object.dirtyKeys()) {
if (dirtyKey === "username" || dirtyKey === "email") {
res.error("User is not allowed to modify " + dirtyKey);
return;
}
}
res.success();
})
In my terminal, I am running parse develop [app] to update files as I change. And when I add the above code it does register a trigger.
Deploying recent changes...
Your changes are now live.
I2014-07-15T03:30:18.108Z]Deployed v63 with triggers:
_User:
before_save
Yet I am still able to, from the rest api using postman, able to update the username and email fields.
I suppose, that the real question should be, is that if I am building my app like this; do I still use these beforeSave() triggers like described? Or is it better to simply make those checks in the method (in this case, the User.update method i created) and validate everything there? Because I cant seem to get the triggers to actually run.

The issue is a common mistake with how for..in works in JavaScript with an array, in your code above dirtyKey will be populated with the index, what you want instead is:
for (var i in req.object.dirtyKeys()) {
var dirtyKey = req.object.dirtyKeys()[i];
if (dirtyKey === "username" || dirtyKey === "email") {
res.error("User is not allowed to modify " + dirtyKey);
return;
}
}

Related

Running some code only if it's tested by cypress

I'm sure I'm missing something very obvious here, but I need to put an if statement in my application code that takes one branch if the current request is from cypress, and the other if not.
(off-topic: I know full well that usually this is a Very-Bad-Idea®, because you want to test the app exactly as it is seen by end users. On the other hand, we live in the real world, with limited time, and sometimes a small bad idea is allowed)
One way seems to be to manually change the user agent or add headers to the cypress calls, I just want to know if there is a very obvious (and better) way of doing this.
When you run the app from a Cypress test, the window object has a property called Cypress.
So inside the app you can test for this property
if (window.Cypress) {
// testing is active
...
} else {
// testing not active
...
}
You can set a flag to indicate you are running the application through cypress.
for example, using the session storage:
Cypress.Commands.add('setFlag', (flag: string, value: string | boolean) => {
cy.window().then((w) => {
w.sessionStorage.setItem('flags.' + flag, value.toString());
});
});
//in the test
before(() => {
cy.setFlag("test_mode", true)
})
And in your app
if(window.sessionStorage.getItem("flags.test_mode"))
//do stuff
else
//do other stuff
You could also implement some sort of service that will manage it.
export class FlagsService {
public get isNotProd() {
return window.location.origin !== "ProdDomain";
}
get<T>(flag: string): T | null {
if (this.isNotProd) {
const key = 'flags.' + flag;
const rawFlag = window.sessionStorage.getItem(key);
if (rawFlag) {
return rawFlag as unknown as T;
}
}
return null;
}

GoogleUser.getAuthResponse() doesn't contain access_token

UPDATE2: I revisited this issue and have solved the problem by carefully following the doco linked below. But first, for those who are struggling with this, you are in good company. There are so many versions of the doco from Google it is confusing! Do you include platform.js or client.js in your html? Do you load gapi.auth or gapi.auth2? Do you use gapi.auth2.render or gapi.auth.authorize, or gapi.auth2.init, and so on.
The way that returns an access_token (as of this article date) is linked below. I managed to get this working by carefully following the guide and reference using platform.js. Other libraries are then dynamically loaded such as client.js using gapi.load('drive', callback).
https://developers.google.com/identity/sign-in/web/listeners
https://developers.google.com/identity/sign-in/web/reference
==== ORIGINAL ISSUE FOR PROSPERITY ====
UPDATE 1:
I've updated the code sample to do a recursive search of the googleUser object. At least this shouldn't break in a subsequent library.
Below is a code snippet to handle an issue where the access_token in the Google gapi.auth2.AuthResponse object is not at the top level... it is hidden :( in the depths of the object!
So it is retrievable, but not at the top level!!?? I've noticed it seems to be a timing issue... once the application is running for a while on subsequent checks, it does contain the access token at the top level!!
var authResponse = _.googleUser.getAuthResponse();
_.id_token = authResponse.id_token; // Always exists
// access_token should also be a param of authResponse
if (authResponse.access_token) {
debug("Worked this time?");
_.access_token = authResponse.access_token;
} else {
// !!! Internal object access !!!
debug("Attempt to get access token from base object.");
_.access_token = _.objRecursiveSearch("access_token", _.googleUser);
if (_.access_token) {
debug("Access token wasn't on authResponse but was on the base object, WTF?");
} else {
debug("Unable to retrieve access token.");
return false;
}
}
_.objRecursiveSearch = function(_for, _in) {
var r;
for (var p in _in) {
if (p === _for) {
return _in[p];
}
if (typeof _in[p] === 'object') {
if ((r = _.objRecursiveSearch(_for, _in[p])) !== null) {
return r;
}
}
}
return null;
}
I'm guessing getAuthResponse somehow provides a callback once it is ready, but I can't see where in the API.
https://developers.google.com/identity/sign-in/web/reference
I know this question is fairly old, but it appears first when googling for ".getAuthResponse() doesn't have access_token," which is how I got here.
So for those of you in 2016 (and maybe later) here's what I have found out
There's a secret argument on .getAuthResponse, not documented anywhere I have found. If you would run the following in your app
console.log(gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse);
You would see that you get the following (copy/pasted from my console)
function (a){if(a)return this.hg;a=.HE;var c=.rf(this.hg);!a.Ph||a.dL||a.Lg||(delete c.access_token,delete c.scope);return c}
This shows that the .getAuthResponse() function looks for an argument, and as far as I can tell doesn't even check its value -- it simply checks if it is there and then returns the whole object. Without that function, the rest of the code runs and we can see very clearly it is deleting two keys: access_token and scope.
Now, if we call this function with and without the argument, we can check the difference in the output. (note: I used JSON.stringify because trying to copy/paste the object from my browser console was causing me some issues).
console.log(JSON.stringify(gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse()));
console.log(JSON.stringify(gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse(true)));
getAuthResponse() object
{
"token_type":"Bearer",
"login_hint":"<Huge mess of letters>",
"expires_in":2112,
"id_token":"<insert your ridiculously long string here>",...}
getAuthResponse(true) object
{
"token_type":"Bearer",
"access_token":"<an actual access token goes here>",
"scope":"<whatever scopes you have authorized>",
"login_hint":"<another mess of letters>",
"expires_in":2112,
"id_token":"<Insert your ridiculously long string here>",
...}
Figured out the fix for this. Turns out that if we don't provide the login scope config in gapi.auth2.init it doesn't return access_token in getAuthResponse. Please call gapi.auth2.init as given below and access_token will be present.
gapi.auth2.init({
client_id: <googleClientID>,
'scope': 'https://www.googleapis.com/auth/plus.login'
})

parse background job each() does not go through all records?

I wrote a cloud job to insert a lowercase version of a user's first name and last name in the user object so that I can perform a search from my client app.
When I did run this job on a dev parse app that had like 40 users it worked great.
When I did run this on my production app however it did not update all of my records. I have a few thousands users at the moment and I was expecting this cloud job to take care of all of them as explained here:
http://blog.parse.com/2013/05/13/launch-and-iterate-parse-data-migrations/
" The new each method on Parse.Query objects allows you to do just that. Even if you have tens of thousands of objects or more in a collection, it will return each one of them, giving you an opportunity to modify them as you see fit."
So then my question is why does this function leave behind over half of my database? It only worked on a bunch of users, maybe a few hundred.
How can I make this function affect my WHOLE DATASET in the User table?
Parse.Cloud.job("migration1", function(request, status) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
// Query for all users
var query = new Parse.Query(Parse.User);
query.each(function(user) {
// Set and save the change
var firstname = user.get("firstname");
if(firstname) {
user.set("searchfirstname", firstname.toLowerCase());
} else {
user.set("searchfirstname", "");
}
var lastname = user.get("lastname");
if(lastname) {
user.set("searchlastname", lastname.toLowerCase());
} else {
user.set("searchlastname", "");
}
return user.save();
}).then(function() {
// Set the job's success status
status.success("search migration completed successfully.");
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong with search migration");
});
});
EDIT:
Ok so when I look at the error logs I see this:
E2014-10-18T15:48:49.984Z] v63: Ran job searchMigration with:
Input: {}
Failed with: TypeError: Cannot call method 'toLowerCase' of undefined
I tried to check for undefined in any way I could think of and I still get the same problem.
What I tried to check for undefined is this:
if(lastname === undefined || lastname === void 0 || typeof lastname == 'undefined') ...
I still get the toLowerCase of undefined error and I think that is why the job does not affect all of my user table since it stops...
Any suggestions?
So, I finally figured out why the job did not go through all the records but it would stop after a few records "randomly"...
I had an AfterSave hook for the user which was triggered at each iteration of the above job...
In that after save it would generate an error at times and make the Job fail.
So, for some reason I thought the after save hook would have not been triggered by a save done on the user while inside of a Job.
That's it. Now it all works.

Passport and Passport Local req.isAuthenticated always returns false

I haven't been able to track this down, but for my set up, isAuthenticated always returns false even after a successful login. Here's the passport code:
req.isAuthenticated = function() {
var property = 'user';
if (this._passport && this._passport.instance._userProperty) {
property = this._passport.instance._userProperty;
}
return (this[property]) ? true : false;
};
But in a quick look around I don't see the _userProperty proeprty anywhere in the local strategy (sorry if I didn't look hard enough), so I suppose that might be why it's always returning false?
I'd leave a code sample of my application code, but I feel it's probably easier to have a quick look at the repo for my work in progress:
passport api token sessionless
Ultimately, my goal is to have logout work properly for that boilerplate project (which it currently it doesn't).
I guess you forgot to put: req.login(...) inside passport.authenticate('local', function(...){}).
See here (at the end of the page)
Apologies if my original question is not that useful in the first place, but...
I found that my combination of passport, passport-local, and passport-local-mongoose, a solution was to simply create an invalidation method on my mongoose Schema (that has the passportLocalMongoose "plugged in", and when my /logout route gets hit I essentially remove that user's token. Here's that method:
Account.statics.invalidateUserToken = function(email, cb) {
var self = this;
this.findOne({email: email}, function(err, usr) {
if(err || !usr) {
console.log('err');
}
usr.token = null;
usr.save(function(err, usr) {
if (err) {
cb(err, null);
} else {
cb(false, 'removed');
}
});
});
};
I presume it's more interesting to see this in context so again please feel free to refer to the repo listed in question...hope this helps someone.
Also, if a core from one of the aformentioned libs wants to suggest a better way I'd of course love to refactor my code to make it idiomatic; if not, this approach seemed to work.

Extjs store.on('save', afterSave(resp));

I have a simple ExtJs (3.4) Grid with a Writer. When the user makes some changes the store is saved to the server as follows:
store.on('save', afterSave(resp));
All is fine. However, I want to get a response as to wheather the record has been saved successfully, failed or an update conflict happed. How to best do this?
Are you using Ext.data.proxy.Ajax to load your stores? If so, you can use the reader property to evaluate and handle the server responses.
Another option would be to make AJAX called directly and handle the responses from there as well
I used exception listener to parse the data as suggested here. But, is this the right way to do this.
Ext.data.DataProxy.addListener('exception', function(proxy, type, action,
options, res) {
if (type == 'response') {
var success = Ext.util.JSON.decode(res.responseText).success;
if (success) {
console.log('UPDATE OK');
} else {
console.log('UPDATE FAILED');
}
}
});

Resources