How can I obtain password hash from ParseUser? - parse-platform

I am currently in the process of migrating all user accounts of my parse-server backend to a 3rd-party SSO provider. The provider allows me to import users with pre-hashed passwords, allowing me to do the transition without needing the users to sign-in to finish the migration process.
I have been having issues trying to obtain the hashed password from the ParseUser object. I can see it in the MongoDB (the _hashed_password field), however I have been unable to extract the password field from the queried object.
I obtain the ParseUser object via the following query (simplified, removed async/await)
const query = new Parse.Query(Parse.User)
query.find({useMasterKey: true}).then(users => {
users.forEach(user => {
// obtain password here + do migration
})
});
I have attempted to get the password via
user.getPassword()
user.get("password")
user.get("_hashed_password")
query.select(["_hashed_password", "password"]).find({useMasterKey: true}).then(...)
The getPassword() function does not exist, but I wanted to try it anyway. the get("password") and get("_hashed_password) returns undefined.
The query.select(...) returns the entire user (except the password), even though I thought it would return either the password or an empty object.
My question is: How can I programatically get the hashed password of a user on the parse platform?
Currently for debugging purposes I am developing this migration as a cloud function. Once I have it working I was planning to move it as a job. I believe this should have no effect on the way the code works, but am leaving this note here just in case anyway.
Thanks in advance for your help.

Thanks to Davi Macêdo, I figured it out.
One has to use aggregate query, however the field _hashed_password gets filtered out by Parse even in aggregate queries, so we need to compute additional field based on the _hashed_password. The following code works:
new Parse.Query(Parse.User).aggregate({
"project": {
"myPassword": "$_hashed_password"
}
})

Related

parse-server: How to differentiate between signUp and regular ParseUser.save in User class BeforeSave?

This is an issue where parse-server behaves differently from Parse.com.
I have anonymous users in my app, and when using ParseUser.signUp (Android SDK) there's an important difference between parse-server and Parse.com. Parse.com would first check for username conflicts, and if there is one, the beforeSave function wouldn't even be called. This allowed me to make some assumptions in the beforeSave code.
Now in parse-server, the beforeSave is always called, and only after I response.success() does it fail to save it.
The problem is that there is some code that shouldn't happen if the internal signUp fails (e.g. duplicate username) but happens anyway since I assume that if beforeSave is called, the username is unique. My solution was to do the check myself via a ParseQuery on the username, but now there's another issue - how do I differentiate between an anonymous user and a new user?
In my app, every new user is automatically saved as an anonymous user. For an anonymous user, there's some things you can't do (change username for example). Now after a while, when he wants to sign up, he enters a username, and in the beforeSave I can't tell if he is trying to change only the username, or is he signing up? If he is trying to sign up, I should allow him to set the username, but if he is just trying to change his username, then I'm suppose to reject the change.
So to summarize: How can I tell in the User class beforeSave if signUp was called or a regular save?
Can you try using the .existed() function in your beforeSave?
Parse.Cloud.afterSave(Parse.User, function(request) {
if(!(request.object.existed())){
//Not sure what anonymous user would return here...
}
});
I couldn't find anything obvious in the documentation, but after analysing the request object of the beforeSave hook, I found a solution:
Verifying if the authData object contains the anonymous key tells us if an anonymous user is saved. Once signed up, authData is undefined:
var authData = request.object.get("authData");
if (authData != undefined && authData.anonymous != undefined) {
console.log("#### User is anonymous");
response.success(); // save and return
return;
}
To figure out if the user has just signed up, you'd compare if the email address was set (not changed but that it was undefined before). You do that by asking request.original for the email:
// object now has an email for the first time
if (request.object.get("email") != undefined &&
request.original.get("email") == undefined) {
console.log("#### User signed up");
…
}
I'm not sure what the recommended way is, but this seems to work to distinguish the anonymous user from the real, signed up user.

Can't save object in Cloud Code

I'm having an issue when running a function in Cloud Code. It is supposed to check the existence of an object and, if it does exist, create a new user:
Parse.Cloud.define("createUser", function(request, response) {
// Query the existing company by id
var query = new Parse.Query(Parse.Object.extend("Company"));
query.equalTo("objectId", request.params.company.existing.id);
query.find().then(function(result){
var user = new Parse.User();
user.set("username", request.params.username);
user.set("password", request.params.username);
user.set("email", request.params.email);
user.set("permissions", ["User"]);
var company = result[0];
user.signUp(null, {
success: function(user){
// Asign company ACL for User write permission
var cACL = company.getACL();
cACL.setWriteAccess(user.id, true);
company.setACL(cACL);
// Save company
company.save();
console.log(company);
// Establish user-company relationship
var cRelation = user.relation("associated");
cRelation.add(company);
// Save user
user.save();
console.log(user);
// Finish
response.success(user);
},
error: function(user, error){
response.error(JSON.stringify({code: -8000, message: "User creation failed"}));
}
});
}, function(error){
response.error(JSON.stringify({code: -8001, message: "Invalid company"}));
});
});
I first query Parse for the existence of said object. If it does exist I create a new user with the parameters received. In the completion block of the user creation I assign the proper ACLs (to the company object) and later on save them. That's when I encounter the first issue: the ACLs are not saved (checked in the dashboard). I console.log the company for debugging purposes and it shows the ACLs are correctly set. So I assume it must be a saving problem.
NOTE: The user is created, but whatever I try to do later doesn't work.
Later on I add this object to a relationship previously defined in the dashboard, but I have the same problem with that: the relationship does not come up in the dashboard, even though when I console.log the object it shows that the relationship was properly set.
I'm lost here. I don't understand why this isn't working and I've read tons of online documentation and still can't find the answer.
Okay, after a day of work I finally found out my problem. I had ACLs set everywhere and I had no privilege for saving the objects I was trying to save. So saving was indeed failing.
I should note that if you are having the same problem I did, you can easily solve it using the Master Key. To do so, you need to call Parse.Cloud.useMasterKey() before executing any requests that must be authenticated.
This only works in Cloud Code, and you should definitely know what you are doing when you use the Master Key because it basically gives read and write privileges to anyone, everywhere, for everything. So make sure your logic is flawless because you might get big security problems if it's not used wisely. As Uncle Ben said: With great power comes great responsibility.
Hope this helps someone.

How can I get a user with a given facebook id using cloud code?

My app requires facebook login, so it is supposed I have all facebook ids from my users. What I want to o in cloud code is a function that given a facebook id (a string), returns the user (or null if no exists). The problem I see is that it seems the facebook id is inside a json structure in the authData column, but I have no idea how to create a query to access to that information. I found this: https://www.parse.com/questions/how-to-get-the-facebook-id-of-an-pfuser-from-a-pfquery-in-ios but no idea about how to use it.
Can you help me with the function I want to create? Thanks in advance.
My comment on Eric's answer expresses my concerns around security, but the cloud-code BeforeSave() function to address my concerns really isn't difficult... for simple use-cases:
Parse.Cloud.beforeSave("MyObject", function(request, response) {
request.object.set("owner_facebook_id", request.user.get("authData").facebook.id);
response.success();
});
In your case, MyObject is the user class, and as long as no users can modify properties on another user object, than this will work pretty well.
However, for the app I was working on, we allowed any user to "like" an object, which incremented a "number_of_likes" property on the object. At that point, this became more tricky, because the user making that request was not, in fact, the owner of the object, so their Facebook_id properties wouldn't have matched.
The work-around was to try and detect whether or not the object had previously existed, and then just make sure the Facebook_id never changed after it was originally created.
We had to access the ORIGINAL object and make sure the newly-saving object had the same Facebook id... it was not exactly trivial, and that lookup actually counts against your request limit. This, combined with a few more edge-cases, caused us to ultimately abandon Parse for that project.
The problem with using authData is that you need a valid active session of that user (or use your master key) to access the data.
If you don't already have a large amount of users, I would recommend creating a new column in your User class that stores the Facebook ID so you can query for it later. That way, you could do something like:
var query = new Parse.Query("User");
query.equalTo("facebookId", request.params.facebookId);
query.find({
success: function(results) {
// do something with the resulting user at results[0], if found
},
error: function() {
response.error("lookup failed");
}
});

Using the Hash::make method to update all entries in table

Since I'm porting an app to Laravel and it's using the Auth Class, I need to change all the passwords in my users table to bycrypt (using Hash::make()).
The thing is that I want to use the usernames as default password (so when the migration is done, my user "Mario" will have a Password of "Mario") — I wanna do this with all the entries of the database via a Migration, but I can't seem to make it, since I don't know how to get the value of the select, hash it, then use it in the update.
Is there any way to do this without using loops? (i.e without making one query per user)
EDIT: Yes, this is impossible to do without loops. I realized that. And #Adrenaxus has the right answer.
Why don't you do something like this:
foreach(User::all() as $user){
$user->password = Hash::make($user->username);
$user->save();
}

Axapta: Validate Access to return value from display method

The Dynamics AX 2009 Best Practice add-in is throwing the following error on a display method override.
"TwC: Validate access to return value from the display/edit method."
Here is my display method.
display ABC_StyleName lookupModuleName(ABC_StyleSettings _ABC_StyleSettings)
{
;
return ABC_Styles::find(_ABC_StyleSettings.StyleID).StyleName;
}
I'm assuming it wants me to check a config or security key before returning a result. Any suggestions/examples on where to start?
Thanks
This is a reminder that you need to consider whether the user should have access to the data you are returning from the function. For table fields, the kernel normally does this for you based on the security groups the user is in and the security keys set on fields.
To check if a user has access to a field, use the hasFieldAccess function. To see how this is used, look at the table methods BankAccountStatement.openingBalance() or CustTable.openInvoiceBalanceMST(). There are other helper functions to check security keys such as hasMenuItemAccess, hasSecuritykeyAccess, and hasTableAccess.
In your case, add this code:
if(!hasFieldAccess(tablenum(ABC_Styles),fieldnum(ABC_Styles,StyleName)))
{
throw error("#SYS57330");
}
Even after you add that code, you will still get the Best Practice error. To tell the compiler you have addressed the issue, you need to add the following comment immediatly before the function declaration:
//BP Deviation Documented

Resources