cruddl icon indicating copy to clipboard operation
cruddl copied to clipboard

Is there a solution to pass the (permissions related) "userId" to mutated objects?

Open frankmayer opened this issue 2 years ago • 3 comments

Scenario: A user creates an object in a collection. The resolvers shoudl inject the "user-id" into the field that is also used for the permissions to display only that user's related records. This is needed in order to prevent the ability of database tampering by clients, who could inject other ids if the clients are to provide said ids. This is also needed for related collections, which when a mutation is creating or updating or removing objects, should pass said ids to all collections so that only user-related objects are mutated or returned in queries.

I have not found this kind of functionality. Is there something that I could use to achieve this or if not, is there anything planned? Thank you very much

frankmayer avatar Nov 07 '22 22:11 frankmayer

Not exactly what you ask for, but you could restrict access to the objects based on the user id with the data-dependent permission feature:

{
    "permissionProfiles": {
        "personal": {
            "permissions": [
                {
                    "roles": ["user"],
                    "access": "readWrite",
                    "restrictions": [
                        {
                            "field": "userId",
                            "claim": "sub"
                        }
                    ]
                }
            ]
        }
    }
}

Then, you need to inject the user name into the auth context:

import { Project } from 'cruddl';
const project = new Project({
    // ...
    getExecutionOptions: ({ context }) => ({ authContext: { authRoles: ['user'], claims: { sub: context.userId } } }),
});

You would need to implement your client so it properly sets the user id in the "userId" fields when creating new objects. cruddl would validate that this is present and set to the correct value. In queries, the userId would automatically be filtered.

Would that work for you?

Yogu avatar Nov 09 '22 12:11 Yogu

I could use this approach minus the client setting any id in the fields. It's probably not good to let clients inject any ids. ~~I could probably though inject the user id into the context by checking session/JWT beforehand. Will try to test in the coming days.~~ A solution that would be needed would be a way to pass data from the context to the resolvers. Maybe something like this:

type User @rootEntity {
    uid: String! @mutationCheckAgainst(contextProperty: "userId")
    username: String!
    email: String
    orders: [Order] @relation
  }

type Order @rootEntity {
    uid: String! @mutationCheckAgainst(contextProperty: "userId")    # needed in every related collection to tighten down permissions 
    user: [User] @relation(inverseOf: "orders")
    addresses: [Address]
  }

Something like that would maybe provide the solution to only allowing valid users to mutate (and query) their own records, where needed.

Thank you very much for your time.

frankmayer avatar Nov 09 '22 19:11 frankmayer

Just to be clear - with the approach I suggested, the client would not be able to choose their id. Providing anything else than their own ID, which is provided by the getExecutionOptions callback, would be an error. It would also be an error if they omitted the id.

So the only downside is that you need to fill the uid field in the client where the mutations are made. It should not affect security.

With regards to the User type in your example - I would not grant users any access to modify user data. Signup, account cancellation and changing user details probably all require additional checks and processes, so it would make more sense to provide them as dedicated services.

Yogu avatar Nov 12 '22 21:11 Yogu