Permissions in Graphcool

We’ve had a first look at the Graphcool framework, then a look at functions with the framework, so let’s now take a look at another important topic for when it comes to creating a GraphQL backend: permissions.

Graphcool’s permission system allows to have full control over nodes and fields as to who is allowed to Read, Create, Update and Delete.

Configuration

The permission configuration for a Graphcool service goes into the graphcool.yml service definition file. By default the permission in place is a wildcard permission that leaves your GraphQL backend wide open:

graphcool.yml

permissions:
  - operation: "*"

To define a permission on a specific operation, simply specify the type name and the operation type in a format like this:

permissions:
  - operation: Todo.create
  - operation: Todo.read
  - operation: Todo.delete

This will allow creating, reading and deleting nodes of type Todo, but updates won’t be permitted. Wildcards can also be used for the operation types. Here all CRUD operations on Todo nodes will be permitted:

permissions:
  - operation: Todo.*

Only authenticated users

If you want certain operations to be allowed only to authenticated users, simply set the authenticated key to true. Here anybody can read Todos, but only authenticated users can create, update or delete Todos:

permissions:
  - operation: Todo.read
  - operation: Todo.create
    authenticated: true
  - operation: Todo.update
    authenticated: true
  - operation: Todo.delete
    authenticated: true

Only specific fields

Even more fine-grained permissions can be defined by specifying permissions only on specific fields within a node. For example, here we’ll only be able to update the name and email fields for a user:

permissions:
  - operation: User.read
    authenticated: true
  - operation: User.create
  - operation: Todo.update
    authenticated: true
    fields:
      - name
      - email
  - operation: Todo.delete
    authenticated: true

Relation connections and disconnections

For types that have a relation (one-to-one, one-to-many or many-to-many), there’s also a set of permissions to define if nodes in the relation can be connected or disconnected. For example, given the following GraphQL schema where we have a one-to-many relation between a User and Posts called UserPosts:

type User @model {
  id: ID! @isUnique
  email: String!
  name: String!
  posts: [Post!]! @relation(name: "UserPosts")
}

type Post @model {
  id: ID! @isUnique
  title: String!
  content: String!
  author: User! @relation(name: "UserPosts")
}

We’d define the permissions for this relation with something like this to allow only authenticated users to connect or disconnect nodes in the relation:

- operation: UserPosts.connect
  authenticated: true
- operation: UserPosts.disconnect
  authenticated: true

…or, we can use the wildcard here since we use the same rule for both connect and disconnect:

- operation: UserPosts.*
  authenticated: true

Permission Queries

So far we’ve seen that we can specify which operation types can be performed and if the user should be authenticated, but we haven’t yet discussed how to limit operations only to the owner of a node, or to only users that have a certain role. Thankfully, there’s an easy way to do that too with permission queries.

You specify a query for a permission with the query key that points to a file with a GraphQL query that should return a boolean. In the following example, any authenticated user can create a new Todo node, but only authenticated users that satisfy a query that we define in a Todo.graphql file will be allowed read, update and delete permission:

permissions:
  - operation: Todo.create
    authenticated: true
  - operation: Todo.read
    authenticated: true
    query: ./src/permissions/Todo.graphql
  - operation: Todo.update
    authenticated: true
    query: ./src/permissions/Todo.graphql
  - operation: Todo.delete
    authenticated: true
    query: ./src/permissions/Todo.graphql

Here’s the content of our Todo.graphql file:

query($node_id: ID!, $user_id: ID!) {
  SomeTodoExists(filter: {
    id: $node_id,
    user: {
      id: $user_id
    }
  })
}

Queries like this in the form of SomeTypeExists return a boolean, and our query has access to the node for which the operation is being performed on and the ID of the currently authenticated user. This way, we can check that the node exists, and that its user relation field has the same ID as the currently logged-in user.


Here’s another example where we want to only allow a user that has the ADMIN role to be able to update a Todo:

- operation: Todo.update
  authenticated: true
  query: ./src/permissions/Todo.graphql:update

Note here how we can refer to a named query in the Todo.graphql file using the :queryName notation.

Here’s the content of our query, where we check that the user exists and has the ADMIN role:

query update($user_id: ID!) {
  SomeUserExists(filter: {
    id: $user_id
    role: ADMIN
  })
}

As an aside, here the roles are defined as an enum as part of our schema definition. The enum definition could look something like this:

enum ROLE {
  ADMIN
  EDITOR
  AUTHOR
}

Permission queries for relations

For relations, permission queries are a little bit different. Given the following permission definition:

- operation: UserTodos.*
  authenticated: true
  query: src/permissions/relations/UserTodos.graphql

Our query to check if the authenticated user is the owner of the relation would look like this:

UserTodos.graphql

query ($user_id: ID!, $userUser_id: ID!) {
  SomeUserExists(filter: {
    AND: [{
      id: $user_id
    }, {
      id: $userUser_id
    }]
  })
}

The query receives the currently authenticated user ($user_id) as well as potentially the id of the node of the left side of the relation and the id of the node on the right side of the relation. Here we only need the id of the node on the left side (the user side of the relation, $userUser_id). We then check that both the currently authenticated user and the user on the left side of the relation have the same id.

Notice the use the filter argument with the AND logic. You can refer to the official docs to learn more about filtering.

  Tweet It
✖ Clear

🕵 Search Results

🔎 Searching...