Open FroeMic opened 6 days ago
Thanks for dropping in @FroeMic!
I can help you rubberduck this a bit. And, if needed, we can incorporate some of the connector authors to help get you where you need to be ๐ค
I would like to check whether a user can execute a specific mutation based on their x-hasura-user-id.
In the vernacular of DDN, Mutations are exposed as Commands
. You can utilize CommandPermissions to restrict which users can utilize which mutations. As an example, you can set a permission using field comparisons like this:
role: user
allowExecution: true
fieldComparison:
field: id
operator: _eq
value:
sessionVariable: x-hasura-user-id
This is a great addition that we'll surface in some of the guides! Knowing this, do you still need zod for validation or even need a custom mutation via the TypeScript connector? If so, we can tackle that next.
@robertjdominguez โ thanks for the fast answer โกโกโก
I feel like I have two issues. I don't want to mix them up โ so let's go step by step if that is ok for you!
In plain-english, what I want to achieve is the following: "If a user has an active membership in a workspace they should be allowed to create a new entry.
Entity Model (simplified): A user can be part of one or many workspaces. The m-n relationship is stored in a table WorkspaceMembership and can be either "ACTIVE" or "REMOVED". Users should be able to CRUD entries. Each entry belongs to exactly one workspace.
In the ModelPermissions I would have achieved the equivalent read permissions check with the following select filter:
kind: ModelPermissions
version: v1
definition:
modelName: Entry
permissions:
- role: admin
select:
filter: null
- role: user
select:
filter:
# only allow access to entries of the user's workspace
relationship:
name: workspace
predicate:
relationship:
name: workspaceMembershipList
predicate:
and:
- fieldComparison:
field: userId
operator: _eq
value:
sessionVariable: x-hasura-user-id
- fieldComparison:
field: status
operator: _eq
value:
literal: "ACTIVE"
To achieve a similar thing via the CommandPermissions function, to check whether the user is part of the workspace they would like to create an entry in, I would need to access the workspaceId
.
My intuition is that writing it to the JWT, isn't quite the right place to do this. With a growing model with more relations, I would need to add an increasing number of variables to the JWT and store more and more state at the session level.
One alternative โ I couldn't figure out whether this is possible โ would be to access the Input data of the command.
Here is parts of the generate hml file for the command.
---
kind: ObjectType
version: v1
definition:
name: CreateEntryInput
fields:
- name: email
type: String!
- name: workspaceId
type: String!
graphql:
typeName: CreateEntryInput
inputTypeName: CreateEntryInputInput
dataConnectorTypeMapping:
- dataConnectorName: my_ts_connector
dataConnectorObjectType: createEntry_input
// skipped
---
kind: Command
version: v1
definition:
name: CreateEntry
outputType: Entry!
arguments:
- name: input
type: CreateEntryInput!
source:
dataConnectorName: my_ts_connector
dataConnectorCommand:
procedure: createEntry
graphql:
rootFieldName: createEntry
rootFieldKind: Mutation
Is this possible?
The naive alternative would be to move this permissions-check into the actual Lambda. My attempt doing this with zod lead to ddn connector introspect my_ts_connector
generating empty Command.hml files (no error message in the console). At this point I am not sure why exactly and how would I best debug this.
I think that defining permission-checks at the Metadata-Layer feels more elegant for me. However, I see how certain mutations might need more complex input validation checks, which would like happen in the lambda.
What I didn't understand from the documentation how one is supposed to best deal with errors in the lambda. I.e., if validation fails should I throw an error? How would this be exposed to the client calling the command? Or do I have to write a ReponseType abstraction that instead of returning Entry
would return Entry | Error
.
I understand that the scenario above is specific to my situation. However, I think there are a few generalized questions that follow from it that might be useful to a broader audience:
I appreciate your help!
My initial experience with ddn was quite smooth for generating the read API and I think it's quite cool stuff!
Alright, thanks for the detail โค๏ธ
Rank-ordered solutions:
workspaceId
in the JWT is going to allow for a metadata-level approach to controlling access.I'm tagging the feature's main author (@paritosh-08) to ensure I'm not leading you down the wrong direction. @paritosh-08 โ can you please confirm this is the best path forward for @FroeMic?
Let's get you sorted on this bit and then we can move onto the other two generalized questions ๐
@FroeMic โ @paritosh-08 has indeed endorsed the above suggestion as the right path forward ๐
Within the plugin's handler, you can write your own logic for doing a lookup and see which โย if any โ workspaceId
a user has. There are various ways to perform this, but we'd recommend creating a service account, which can then make the necessary queries against your API.
Can you let me know your thoughts, and then we'll tackle your other questions?
Describe the improvement
When trying to follow the docs on how to create a custom mutation with the typescript connector I stumbled across several problems, when trying to implement validation:
It would be great to have a reference case how to implement validation. For example,
1. How to validate the input for a mutation (types etc)
Zod-Validation
I didn't manage to get Zod validation working in function.ts.
Importing the zod schema in function.ts creates to a faulty generation when running "ddn connector introspect my_ts_connector"
2. How to check permissions the permission โ i.e, who can execute a specific mutation
For example, similar to the
ModelPermissions
, I would like to check whether a user can execute a specific mutation based on their x-hasura-user-id.For example, a user should only be able to add an object (with a foreign reference workspace_id) to a workspace, if they are also an active member of said workspace.
How can I make that check and where should I do it?
Location of the improvement
https://hasura.io/docs/3.0/getting-started/build/mutate-data?db=PostgreSQL
Additional context (optional)
I hope that's the correct place to post that!
I feel like creating the GraphQL Query API was all really smooth. Creating mutations has been much more difficult.