In this case, only the user itself should have access to its own User object.
Currently the ACL is set in the Cloud Code afterSave trigger, right after the user is created:
Parse.Cloud.afterSave("_User", async (request) => {
const user = request.object;
if (!user.existed()) {
user.setACL(new Parse.ACL(user));
await user.save();
}
});
Is it possible to do that in beforeSave trigger (to save a DB write)?
I took a look at the Parse Server code and it should be possible. I was afraid that the default ACL could override what you've set in the beforeSave trigger but that's not the case. As you can see here it maintains what is set beforehand and you can actually send this setting even from the client.
Setting an empty ACL in beforeSave will ensure that only the user is granted access, because a user is granted access to their own _User object by default:
request.object.setACL(new Parse.ACL());
Yep, just be sure not to call user.save() in a beforeSave
Related
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
});
I want to develop dynamic roles authorization using .net core webAPI, my structure is that user have one role and the role have some function or features to access
my question is there is any way yo get the function name where authorization policies applied
as example I have the following code
[Authorize(Roles = "Admin", Policy = "isHasPermission")]
public async Task<IActionResult> GetAllAsync()
{
var users = await _userService.GetAllAsync();
var userDtos = _mapper.Map<IList<UserDto>>(users);
return Ok(DataMessage.Data(new { users = userDtos }));
//return Ok(userDtos);
}
and my policy is something like that
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
isHasPermissionRequirement requirement)
{
/*
CAN I GET THE FUNCTION NAME "GetAllAsync" HERE!
TO VALIDATE IF IT IS ONE OF USER'S FEATURE
*/
return await Task.CompletedTask;
}
So that I need to get the function name in the policy to validate user's permissions, if it is possible or not?
You are doing it backwards: The way policies work is that you say that a certain action has requirements. It is not a valid requirement to then circle back to where the policy is used. Policies should be completely separate from what you are trying to access. If a certain thing specifies a policy, then just the presense of the policy should be all that’s necessary.
If you want to have your logic actually check what you are trying to access, then you could look into authorization filters instead. When they are called, they pass an AuthorizationFilterContext which also contains information about the route and action the user is trying to access. With that, you can get the action name for example using (context.ActionDescriptor as ControllerActionDescriptor).ActionName.
I am trying to migrate users to Cognito when they sign in the first time. For this I wrote a lambda function that does call an API to check if the users exist in db or not ? if the user exists, it will be created in cognito but I am not sure how do I tell the application that user is created and it should allow the user to login .
Here is the code in c#:
public async Task<Stream> FunctionHandlerAsync(Stream stream, ILambdaContext context)
{
RootObject rootObj = DeserializeStream(stream);
User user = new User(rootObj.userName, rootObj.request.password);
ApiResponse apiResponse = await MobileAuthenticateAsync(user.UserName, user.Password);
// Considering apiResponse returns "user authenticated", we create the user in //cognito. This is working.
// How do I send response back to Application so it knows that user is // //created and authenticated and should be allowed to login.
//Before returning stream, I am setting following 2 status.
rootObj.response.finalUserStatus = "CONFIRMED"; // is this correct ?
rootObj.response.messageAction = "SUPPRESS";
return SerializeToStream(rootObj);;
}
You're pretty close.
You can see the full documentation on the Migrate User Lambda Trigger page, however in short you need your response to look like:
{
response: {
userAttributes: {
email: 'user#example.com',
email_verified: true,
custom:myAttribute: 123,
},
finalUserStatus: 'CONFIRMED',
messageAction: 'SUPPRESS',
forceAliasCreation: false,
}
}
Where:
userAttribute: this is a dictionary/map of the user's attributes keys in cognito (note that any custom attributes need to be prefixed with custom:), to the values from the system you're migrating from. You do not need to provide all of these, although if you're using an email alias you may want to set email_verified: true to prevent the user having to re-verify their e-mail address.
finalUserStatus: if you set this to CONFIRMED then the user will not have to re-confirm their email address/phone number, which is probably a sensible default. If you are concerned that the password is given as plain-text to cognito this first-time, you can instead use RESET_REQUIRED to force them to change their password on first sign-in.
messageAction: should probably be SUPPRESS unless you want to send them a welcome email on migration.
forceAliasCreation: is important only if you're using email aliases, as it stops users who manage to sign-up into cognito being replaced on migration.
If you respond with this (keeping the rest of the original rootObj is convenient but not required then the user will migrated with attributes as specified.
If you throw (or fail to respond with the correct event shape) then the migration lambda fails and the user is told that they couldn't migrated. For example, because they do not exist in your old user database, or they haven't provided the right credentials.
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()
I've set up my app to enable email verification and the emails come through fine. Trouble is, when I make a call to retrieve a User object, either with Parse.User.current() or by querying by id, the response does not contain the emailVerified field. I can't therefore check if the user is email verified or not.
I've tried this both in client side code and in cloud code with the same result.
You can try this out in your own code with a very simple snippet in the console:
var user = Parse.User.logIn("your_username", "your_password", {
success: function(user) {
console.log(user)
}
});
That field seems to be protected, the only solution I can think of would be to query for where the value is true using some variant of where and check if you get a user back or not. If user is null, they haven't verified their email.