npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

graphql-sword

v1.1.0

Published

Add a permission layer to your GraphQL Server with graphql-shield

Downloads

7

Readme

GraphQL Sword

npm version Commitizen friendly

The perfect companion of graphql-shield to manage your permission layer following BREAD convention

Overview

GraphQL Sword is a middleware for a GraphQL Server which helps you create a permission layer with graphql-shield, but following the BREAD permission structure. The actual behavior is so inspired by the permissions logic from GCF (aka GraphCool Framework).

Installation

yarn add graphql-sword graphql-middleware

Usage

Apollo Server v2 (basic implementation)

import * as express from 'express'
import * as jwt from 'jsonwebtoken'
import { ApolloServer } from 'apollo-server-express'
import { makeExecutableSchema } from 'graphql-tools'
import { applyMiddleware } from 'graphql-middleware'
import { permissions, IPermission } from 'graphql-sword'

const app: express.Express = express()

const typeDefs = `
  type Query {
    frontPage: [Fruit!]!
    fruits: [Fruit!]!
    customers: [Customer!]!
  }

  type Mutation {
    addFruitToBasket: Boolean!
  }

  type Fruit {
    name: String!
    count: Int!
  }

  type Customer {
    id: ID!
    basket: [Fruit!]!
  }
`

const resolvers = {
  Query: {
    frontPage: () => [{name: "orange", count: 10}, {name: "apple", count: 1}]
  }
}

const schema = makeExecutableSchema({
  resolvers,
  typeDefs,
})

// Queries
// You can directly check information from the context
const isAdminQuery = async (ctx): boolean => return ctx.user.role === 'admin'

// Or request Prisma to verify a specific rule
const isEditorQuery = async (ctx, node_id): boolean => 
  ctx.db.exists.User<boolean>({
    id: node_id,
    role: 'editor',
  })

const isCustomer = async (ctx, node_id): boolean =>
  ctx.db.exists.Customer<boolean>({
    id: node_id,
  })

// Permissions
const permissionsSetup: IPermission[] = [
  {
    operation: 'Fruit.Browse',
    alias: 'frontPage',
  },
  {
    operation: 'Fruit.Browse',
    authenticated: true,
    query: isEditorQuery,
  },
  {
    operation: 'Customer.Browse',
    authenticated: true,
    query: isAdminQuery,
  },
  {
    operation: 'Customer.Edit',
    authenticated: true,
    alias: 'addFruitToBasket',
    fields: ['basket'],
    query: isCustomer,
  },
  // Will be available later to add rule on a type directly
  // {
  //   operation: 'Customer.*',
  //   query: isAdminQuery,
  // },
]

// Middleware
const permissionsMiddlewareInternal = permissions(
  permissionsSetup,
)

const schemaWithMiddleware = applyMiddleware(
  schema,
  permissionsMiddlewareInternal,
)

const server = new ApolloServer({
  schema: schemaWithMiddleware,
  context: ctx => ({
    ...ctx,
    db, // any Prisma instance
    user: (<any>ctx.req).user || null,
  }),
})

// Add some code here to handle JWT / context.user
// See some example from `prisma`, `graphql-yoga`, `apollo-server`
// or `graphql-authentication` repositories

server.applyMiddleware({ app, path: '/' });

app
  .listen(options, () => console.info(
    `🚀  Server ready at http://localhost:${options.port}${server.graphqlPath}`,
  ))

API

Types

// Permission
function permission(
  permissionsRules: IPermission[],
  _options?: IOptions,
): IMiddlewareGenerator<any, any, any>

interface IPermission {
  operation: string
  authenticated?: boolean
  alias?: string
  fields?: string[]
  query?: IPermissionQuery
  cache?: ICacheOptions
}

type IAction = 'Browse' | 'Read' | 'Edit' | 'Add' | 'Delete' | '*'
type ICache = ICacheOptions // inherited from graphql-shield
type IPermissionQuery = (ctx: any, T: any) => Promise<boolean>

interface IOptions {
  debug?: boolean
  authenticatedRule?: IPermissionRule
}

interface IPermissionRule {
  (
    args?: IPermissionArgs,
  ): Rule
}

interface IPermissionArgs {
  query?: IPermissionQuery
  fields?: string[]
  cache?: ICacheOptions
  action?: IAction
}

permissions(permissionsRules, options?)

Generates GraphQL Middleware layer from your permissions.

permissionsRules

An array of your wanted permissions. A permissions must contain the operation name and at lease one option. Actually, a permission is applied on query (Query or Mutation). It will be available later to apply it on a type or field.

Limitations

Permissions are appended to a query directly to enforce the validation before the server make the request. If you apply the fields options to a permission, it will also monitor it before the request.

Because when a permission is applied to a type (or one of its fields), the verification will be done on the response of the request (so the result of the resolver), because the resolver need to populate the parent parameter from (parent, args, context, info) => { ... }.

Possibilities

More example are availabe here.

options

| Property | Required | Default | Description | | ----------------- | -------- | ------------------------ | ---------------------------------------- | | debug | false | false | Enable debug mode for graphql-shield. | | authenticatedRule | false | defaultAuthenticatedRule | Allow the specify a custom rule for the validtion of the authencated option |

By default, graphql-sword control the authenticated option by checking if user and user.id properties are available inside the context. The user property is actullay set during the initialization of the GraphQL Server. In the example given above, the user is set from ctx.req which represent the Request object from express handled by apollo-server. For example, with graphql-yoga, it must be get from ctx.request. But you can also add your own logic to get the user from a JWT token or the server session.

Roadmap

V1.1

  • Improve fields checking for the input args for Add and Edit action, instead of the output fields

V1.2

  • Manage overrided permissions with or() rule from graph-shield this problem can happen with this config, because there is 2 permissions which result on the same request name
    {
      operation: 'User.Read',
      authenticated: true
    },
    {
      operation: 'User.Read',
      query: isOwner,
    }
  • Think about an deny option to reflect of the not() rule from graphql-shield
  • Think about an group option to reflect of the and() rule from graphql-shield

V1.3

  • Allow CRUD keywords (Create, Read, Update, Delete) to generate default resolver name like createUser or updateUser
  • Allow string array for alias option to apply the same permission on several queries

V2

  • Allow to apply a permission on a type directly (and not only to the operation Query or Mutation)
  • Will need to use fragments on the resolvers with the release of graphql-shield v3
  • Add the option exclude: string[] to the model permission to add an expection on some request

VX (roadmap to be determined)

  • Allow to override the defaultRule applied when the option query is used (surely with an option rule)

Licence

MIT @ Johann Pinson The icon used in the logo is made from Icon Fonts and is licensed by CC BY 3.0