Parse Server Access PFUser in BeforeSave Trigger - parse-platform

I need to check a property of my PFUser's in beforeSave triggers for each of my classes to determine if that user should be allowed to edit the piece of data they are attempting to edit.
For example, if a non-admin PFUser is attempting to edit or add to a class they shouldn't be allowed to, I want to prevent that in the beforeSave trigger. I access the keys being edited using dirtyKeys.
Parse-Server doesn't support .currentUser() like the old Parse server used to. How can I access the PFUser who is making the request? Is there a way to do it besides through session tokens?
Parse.Cloud.beforeSave("Class", function(request, response) {
//Get the keys that're being edited and iterate through them
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
//Allow or don't allow editing of each key
if (userObject.get("<KEY>")) {
console.log('Class before save trigger IS key');
//ADD CLASS SPECIFIC FUNCTIONALITY HERE
} else {
console.log('Class before save trigger NOT key');
//ADD CLASS SPECIFIC FUNCTIONALITY HERE
}
}
});

Turns out the answer is much more obvious than I anticipated and was in the docs but I overlooked it despite my searching.
Since Parse.User.current() isn't working in Parse Server, the replacement is simply request.user. I was able to easily access all the data I needed from this and am good to go.
var user = request.user; // request.user replaces Parse.User.current()

Related

Call cloud code without logged in user Parse Server JS SDK

Is it possible to call a cloud function that returns objects without having a current user? The iOS and Android SDKs support anonymous users but I'm asking specifically for JavaScript.
I'd like to allow it so that anyone who visits my web app can read objects without having to sign in. I'm using Back4App.
Yes. You can call a cloud code function no matter the user is logged in or not. Inside the cloud function you can check the user property of the request object to check if the user is either logged in or not. In the case that your user is not logged in and you want to query a class which requires user permission, you can use the useMasterKey option.
Parse.Cloud.define('myFunction', async req => {
const { user, isMaster } = req;
if (isMater) {
// the cloud code function was called using master key
} else if (user) {
// the cloud code function was called by an authenticated user
} else {
// the cloud code function was called without passing master key nor session token - not authenticated user
}
const obj = new Parse.Object('MyClass');
await obj.save(null, { useMasterKey: true }); // No matter if the user is authenticated or not, it bypasses all required permissions - you need to know what you are doing since anyone can fire this function
const query = new Parse.Query('MyClass');
return query.find({ useMasterKey: true }) // No matter if the user is authenticated or not, it bypasses all required permissions - you need to know what you are doing since anyone can fire this function
});

KeystoneJS: How to set a field to receive randomly generated value?

I'm creating a model that I will use to authenticate users for API access, and I have a secret field where I want to store a Base64 encoded uuid/v4 generated value.
I went through the different field types and options, but still not seeing how I could achieve this.
Is there a way to hook in model instance creation, and set the value of my secret field ?
Yes, you can use the pre hooks.
In your situation, the basics would be:
AuthenticationModel.schema.pre("save", function(next) {
const secretValue = generateSecretValue();
this.secret = secretValue;
next();
});
That would go before your final AuthenticationModel.register(); in your model.js file.
This is how I set it up, also with the pre-save hook. My problem before was that I was getting the same random number again until I restarted the server.
Store.schema.pre('save', function (next) {
if (!this.updateId && this.isNew) {
// generates a random ID when the item is created
this.updateId = Math.random().toString(36).slice(-8);
}
next();
});
Using this.isNew was also useful in my case.

Make "email" for PFUser private

Is there a way to set private the email property of a PFUser class, without having to set private the entire class?
It's been requested for a few years over and over again to add the ability to do this but the Parse team has yet to release anything.
If you place the email in a PrivateUserData subclass, the email can be private, but the password reset function can no longer work. Unless you set the email of the User object to be your own email and do something about it... :)
The following could make it work:
You could make a PublicUserData subclass and place all the user information that you intend to be able to be publicly read in that class, such as the username. Then, make the User subclass private. Anytime you want to access the User subclass to modify information, just log in the user.
var PublicUserData = Parse.Object.extend("PublicUserData");
var publicData = new PublicUserData();
publicData.set("username", username);
publicData.set("userId", user.id);
publicData.save(null, {
success: function(projectData) {
},
error: function(projectData, error) {
alert(error.message);
}
});

Updating existing Parse object in Cloud Code

I am using Parse for my backend and want to search for existing friend requests and update those instead of creating new ones (if there is already an existing one).
I thought I figured out how to do it but when I submit new friend requests they get created as new objects instead of updating the old one, even though I found an existing request.
Here is the code I am using:
Parse.Cloud.beforeSave("FriendRequest", function(request, response) {
//search for an existing friend request with the same "from" and "to"
var query = new Parse.Query("FriendRequest");
query.equalTo("from", request.object.get("from"))
.equalTo("to", request.object.get("to"));
query.find({
success: function(results) {
if(results.length > 0)
{
var result = results[0];
//the new request id is undefined as expected
console.log("request id: " + request.object.id);
//the result id is valid for an object in the db as expected
console.log("result id: " + results[0].id);
//set the id of the request to the id of the existing db object
request.object.id = results[0].id;
//the valid id is now in the request object id
console.log("request id: " + request.object.id);
//after response.success, the database shows a new entry
//with a different id
//instead of updating the existing entry
response.success();
}
}
});
});
There isn't a lot going on here. The query does come back successful with the correct entry in the database. I can confirm that I get the correct objectId for the existing item in the database. Any thoughts or ideas are appreciated!
You can't manually set the objectId of an object.
If you want beforeSave to NOT create a new object (which is what you're about to do when beforeSave is called), you need to manually update the existing object and then respond with a failure. If you respond with response.success(), the object will be saved normally.
In your code, you don't seem to make any changes to the existing object. All you really need to do is to return response.error (https://parse.com/docs/cloud_code_guide#functions-onsave)
Of course, you should also handle this in your code somehow. Either by alerting the user, or handling it silently.
However; why does your code attempt to save a new friend request if one already exist? Your app should know that one exists and disable the friend request button or whatever the UI offers.

ASP.NET MVC, Forms Authentication, and Session

I would like to execute the below line when the user logs in so that I have access to the MembershipUser object. However I am having a hard time figuring out when to set it.
Session["User"] = Membership.GetUser();
So far I've tried...
Application_AcquireRequestState
Application_BeginRequest
FormsAuthentication_OnAuthenticate
For each the session state isn't necessarily available.
Manually calling it in the log-in page is easy, but I need to have it work when automatically logging in using cookies as well.
If all you want do is store arbitrary data along with the username, there is an open source project called FormsAuthenticationExtensions that allows you to do exactly this in a very simple manner:
In your Login action you would store your data like this:
var ticketData = new NameValueCollection
{
{ "name", user.FullName },
{ "emailAddress", user.EmailAddress }
};
new FormsAuthentication().SetAuthCookie(user.UserId, true, ticketData);
And you read it back like this:
var ticketData = ((FormsIdentity) User.Identity).Ticket.GetStructuredUserData();
var name = ticketData["name"];
var emailAddress = ticketData["emailAddress"];
Data is stored in the same cookie holding the authentication ticket, so it will be available for as long as the user is logged in.
Project page: http://formsauthext.codeplex.com/
Nuget: http://nuget.org/List/Packages/FormsAuthenticationExtensions
Why? You can access Membership.GetUser from anywhere. It's a static method. What is the point of placing a value you can access from anywhere in a place you can access from anywhere?

Resources