0.2.3 • Published 3 years ago

neo4j-deepauth v0.2.3

Weekly downloads
7
License
Apache-2.0
Repository
github
Last release
3 years ago

neo4j-graphql-deepauth

Directive-based support for fine-grained access control in neo4j-graphql-js GraphQL endpoints (i.e. GRANDstack apps). Also enables support in any non-neo4j-graphql-js GraphQL endpoint that exposes a filter field argument with nested relational filtering.

Breaking Changes from the v0.1.0-alpha

In order to reinforce that the path argument should be the string representation of a valid _<Type>Filter Input corresponding to the ObjectType Definition on which the directive is being applied, the path string is no longer wrapped with brackets while processing directive arguments and applying the authorization path to the filter. Users upgrading from v0.0.1-alpha should wrap the inner contents of their path arguments in brackets as follows:

path: """ userId: "$user_id" """ would become path: """{ userId: "$user_id" }""".

Apologies for any inconvenience and contradicting a decision made earlier.

Using the neo4j-deepauth package

1. Install package via NPM or Yarn

yarn add neo4j-deepauth or npm install neo4j-deepauth

2. Add schema definition for @deepAuth directive to your SDL.

Your type definitions should include the following:

const typeDefs = `
  # Other TypeDefs you defined before

  directive @deepAuth(
    path: String
    variables: [String]
  ) on OBJECT
`

Note that, under its current implementation, the behavior of @deepAuth will only be applied to Objects. Union or Interface types & field-level access control are yet to be implemented.

3. Add directive to user-defined types.

Modify your previously-defined type definitions by including @deepAuth on any Object you want it to apply to. Using our To-Do example, that might look like:

const typeDefs = `

type User @deepAuth(
  path: """{ OR: [{userId: "$user_id"},
                {friends_some: {userId: "$user_id"}}] }""",
  variables: ["$user_id"]
){
  userId: ID!
  firstName: String
  lastName: String
  email: String!
  friends: [User] @relation(name: "FRIENDS_WITH", direction: "OUT")
  taskList: [Task] @relation(name: "TO_DO", direction: "OUT")
  visibleTasks: [Task] @relation(name: "CAN_READ", direction: "IN")
}

type Task @deepAuth(
  path: """{ visibleTo_some: {userId: "$user_id"} }"""
  variables: ["$user_id"]
) {
  taskId: ID!
  name: String!
  details: String
  location: Point
  complete: Boolean!
  assignedTo: User @relation(name: "TO_DO", direction: "IN")
  visibleTo: [User] @relation(name: "CAN_READ", direction: "OUT")
}

# ...Directive definition from above
`

Here we've limited access to Users if: a) the client is the User; or b) the client is friends with the User. And we've limited access to Tasks if and only if the client's User has a CAN_READ relationship to the Task. This is not the only or best authorization structure, just a simple example.

Please note that the path argument strongly corresponds to the filter argument Input Types that would define the existence of the ACL structure, and the augmented schema can be validated using validateDeepAuthSchema() to ensure each path corresponds to a valid _<Type>Filter Input Type definition (i.e. the schema object you get from running makeAugmentedSchema with neo4j-graphql-js). As such, it must be written enclosed by brackets at the outermost level. This is a change from formerly requiring just path not { path }.

4. Modify resolvers and request context

Unless or until @deepAuth is integrated as a broader feature into neo4j-graphql-js, we will not be able to rely on the automatically-generated resolvers. We will have to modify them ourselves.

Per the GRANDstack docs, "inside each resolver, use neo4j-graphql() to generate the Cypher required to resolve the GraphQL query, passing through the query arguments, context and resolveInfo objects." This would normally look like:

import { neo4jgraphql } from "neo4j-graphql-js";

const resolvers = {
  // entry point to GraphQL service
  Query: {
    User(object, params, ctx, resolveInfo) {
      return neo4jgraphql(object, params, ctx, resolveInfo);
    },
    Task(object, params, ctx, resolveInfo) {
      return neo4jgraphql(object, params, ctx, resolveInfo);
    },
  }
};

As alluded to above, we must modify these resolvers to replace the resolveInfo.operation and resolveInfo.fragments used by neo4jgraphql() with the pieces of your transformed query. That might look something like:

import { neo4jgraphql } from "neo4j-graphql-js";
import { applyDeepAuth } from "neo4j-deepauth";

const resolvers = {
  // entry point to GraphQL service
  Query: {
    User(object, params, ctx, resolveInfo) {
      const authResolveInfo = applyDeepAuth(params, ctx, resolveInfo);
      return neo4jgraphql(object, params, ctx, authResolveInfo);
    },
    Task(object, params, ctx, resolveInfo) {
      const authResolveInfo = applyDeepAuth(params, ctx, resolveInfo);
      return neo4jgraphql(object, params, ctx, authResolveInfo);
    },
  }
};

If you use any variables in your @deepAuth directives, you must define them within your request context with the key as it appears in your variables argument. Here is an example of how to add values to the deepAuthParams in the context using ApolloServer:

const server = new ApolloServer({
  context: ({req}) => ({
    driver,
    deepAuthParams: {
      $user_id: req.user.id
    }
  })
})

5. Update Custom Mutations

The automatically-generated mutations will not currently respect or enforce the authorization paths provided on @deepAuth. Also, it will often be helpful or necessary to create/delete additional authorization nodes/relationships in the same transaction as a Create/Delete mutation.

For these reasons, you will need to create your own custom mutation resolvers for pretty much any Type that has @deepAuth applied or has a relationship to a @deepAuthed Type.

Design Goals & Thought Process

As neo4j-deepauth was being developed, a series of articles was posted to Dev.to explaining the motivation behind this package and some of the design choices that were made. You can find them here: Access Control with GRANDstack

Example

An example of neo4j-deepauth use can be found at github.com/imkleats/neo4j-deepauth-example

0.2.4-beta.1

3 years ago

0.2.4-beta.0

3 years ago

0.2.3

4 years ago

0.2.2

4 years ago

0.2.1

4 years ago

0.2.1-beta.1

4 years ago

0.2.1-beta.0

4 years ago

0.2.0

4 years ago

0.2.0-beta.5

4 years ago

0.2.0-beta.4

4 years ago

0.2.0-beta.3

4 years ago

0.2.0-beta.2

4 years ago

0.2.0-beta.1

4 years ago

0.2.0-beta.0

4 years ago

0.1.3

4 years ago

0.1.0

4 years ago

0.1.2

4 years ago

0.1.1

4 years ago

0.1.0-alpha

4 years ago