Skip to content

Disable record creation from client code. #4894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
srameshr opened this issue Jul 14, 2018 · 17 comments
Closed

Disable record creation from client code. #4894

srameshr opened this issue Jul 14, 2018 · 17 comments

Comments

@srameshr
Copy link
Contributor

srameshr commented Jul 14, 2018

Issue Description

Is it possible to disable record creation on any Class via client code by a simple configuration on the server?
Basically, I want save or saveAll operations to be triggered by cloud code only, without using any beforeSave trigger on any Class.

Steps to reproduce

N/A

Expected Results

Do this via, chrome JS console.

const customClass = Parse.Object.extend('CustomClass');
const customInstance = new customClass();
customInstance.set('foo', 'bar');
customInstance.save();

Actual Outcome

N/A

Environment Setup

  • Server

    • parse-server version (Be specific! Don't say 'latest'.) : 3.x
    • Operating System: Any
    • Hardware: Any
    • Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): All
  • Database

    • MongoDB version: 3.x
    • Storage engine: Any
    • Hardware: Any
    • Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): All

Logs/Trace

Include all relevant logs. You can turn on additional logging by configuring VERBOSE=1 in your environment.

@georgesjamous
Copy link
Contributor

How about using CLP with masterKey only write access ?

@srameshr
Copy link
Contributor Author

srameshr commented Jul 20, 2018

Destroys the point of CLP, if I just set every public write to false.
Right now this is what I am doing. For every save or saveAll call from the cloud code, I am setting a key called serverAuthKey.
The value of that key is set from .env file and I check for this key to match the right value in beforeSave.

Demo code:

Parse.Cloud.beforeSave('MyCustomClass', (req, res) => {
   if (req.object.get('serverAuthKey') === process.env.SECRET_SERVER_KEY) {
     req.object.unset('serverAuthKey'); // So this wont get saved to the db.
     res.success();  
   } else {
     req.object.unset('serverAuthKey'); // Regardless remove that key
     res.error('Authorization failure... bad bad boy'); 
   }
});

And during the call, from cloud code:

myCustomClassInstance.save({
  ...body,
  serverAuthKey: process.env.SECRET_SERVER_KEY
})

And if someone tries to do a save call from a client, it obviously fails because of SECRET_SERVER_KEY

@georgesjamous
Copy link
Contributor

Yes, I just noticed that using masterKey with CLP will also affect individual object ACL since its using masterKey, I apologize for my mistake.
And yes, just remembered that in one of my projects, I did the exact same thing you did to make sure the request came from CloudCode

@flovilmart
Copy link
Contributor

Why can’t you use CLP on this case? Your secret key is basically a master key. If you don’t want to share that masterKey, then that’s another issue.

As for your snippet, this works with cloud code, and I don’t see any issue with it

@srameshr
Copy link
Contributor Author

Can you show a snippet of how you try to achieve this with CLP.

@georgesjamous
Copy link
Contributor

georgesjamous commented Jul 21, 2018

@flovilmart
wouldn't using CLP with master key and updating objects bypass the ACL of the object ?

@flovilmart
Copy link
Contributor

CLP are processed before the ACL’s. If you restrict object creation or object to master key only, you’ll need to use the masterKey to update the object. THhen obviously the ACL Will not matter as the masterKey will be used.

@georgesjamous
Copy link
Contributor

Yes, that is what I meant.
So, for now, the only solution would be yours @srameshr

@flovilmart
Copy link
Contributor

The server auth key is basically a master key in the sense it’s being used. Not sure there anything to do in parse server. You could also use a custom header, as they are forwarded to all cloud code functions

@srameshr
Copy link
Contributor Author

srameshr commented Jul 21, 2018

I don't understand why things get closed without everyone coming to a conclusion. If I see there is a conclusion or if my requirements are met, I will close it myself.

Setting CLP as mentioned in the docs does not work. If I navigate to my desired Class from the data browser and under Seucrity -> Advanced and if I set addFields to false (for public role), it does not meet the requirements as I have specified in the question, because I will still be able to add records from client.

@georgesjamous
Copy link
Contributor

georgesjamous commented Jul 21, 2018

@srameshr this is my input on this.
First, addFields in CLP restricts additional fields from being created in a class.
(ie. new keys for objects to be added)
If you want to restrict additional object from being created completely, you have to restrict (uncheck)
Write -> Create, this will prevent objects from being created without a masterKey. Thus having the operating flow you want.
You can then only create objects by passing masterKey via a cloud function.

Now, this solution won't work if you want to restrict Write completely from Client endpoint because you will also have to uncheck Update and Delete. And when using masterKey to update or delete an object you will have neglected the object's ACL, thus having users be able to edit objects of other users (or roles)

The solution you have provided to do so in a cloud function is valid and the best one for now for record creation.

Is it possible to disable record creation on any Class via client code by a simple configuration on the server?

@georgesjamous
Copy link
Contributor

georgesjamous commented Jul 21, 2018

If you decide to go ahead with what you proposed and already doing, I suggest you create a helper module that secures any function you want with an internally generated key.
You could then register your cloud function like this.

Parse.Cloud.beforeSave('MyCustomClass', (req, res) => secure(req, resp, theSecuredFunction))

Where secure could do most of this work and then calls 'theSecuredFunction'

if (req.object.get('serverAuthKey') === process.env.SECRET_SERVER_KEY) {
req.object.unset('serverAuthKey'); // So this wont get saved to the db.
res.success();
} else {
req.object.unset('serverAuthKey'); // Regardless remove that key
res.error('Authorization failure... bad bad boy');
}

And when you want to save an object, you do
secureSave(object) where secure save could be defined somewhere else and just adds your secret key. (the one you check for in secure)

Hope it makes sense

@srameshr
Copy link
Contributor Author

srameshr commented Jul 21, 2018

@georgesjamous I had a similar one, for the sake of simplicity and brevity I chose the above example.
So, this looks good for now. The addFields was a miscommunication while typing.

@flovilmart
Copy link
Contributor

@srameshr I closed the issue because this is not a bug with parse server but an implementation detail to which you already have a proper solution.
We provided multiple possible options, using CLP and I don’t see why this issue should stay open. If you need help for your implementation, you can reach out on stackoverflow or hitter and discuss your solution with other devs.

@srameshr
Copy link
Contributor Author

srameshr commented Jul 22, 2018

@flovilmart If you read my question again, you will see that I was asking for a configuration, possibly while instantiating new ParseServer({}). So, this ideally stands as a feature request. The discussion was on workarounds for the feature and not the implementation of the feature itself.

@oallouch
Copy link
Contributor

Maybe this request is liked to this one : parse-community/Parse-SDK-JS#583

@flovilmart
Copy link
Contributor

flovilmart commented Jul 23, 2018

I’m not sure we’ll implement a system where only the masterKey would work, as you can implement it through CLP. As always, if it’s a feature that you need, we’re open for pull requests. As it stands, i believe this is an unnecessary burden. In your case you’re Using another ‘secret’ (that should be passed as a header instead of an object property) which is very specific for your use case, and doesn’t follow the rest of the philosophy of this project.

On a small note, you can easily wrap all your cloud code in a secure method that checks for your custom secret

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants