| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | const { SchemaDirectiveVisitor } = require('graphql-tools')const { defaultFieldResolver } = require('graphql')const _ = require('lodash')class AuthDirective extends SchemaDirectiveVisitor {  visitObject(type) {    this.ensureFieldsWrapped(type)    type._requiredAuthScopes = this.args.requires  }  // Visitor methods for nested types like fields and arguments  // also receive a details object that provides information about  // the parent and grandparent types.  visitFieldDefinition(field, details) {    this.ensureFieldsWrapped(details.objectType)    field._requiredAuthScopes = this.args.requires  }  visitArgumentDefinition(argument, details) {    this.ensureFieldsWrapped(details.objectType)    argument._requiredAuthScopes = this.args.requires  }  ensureFieldsWrapped(objectType) {    // Mark the GraphQLObjectType object to avoid re-wrapping:    if (objectType._authFieldsWrapped) return    objectType._authFieldsWrapped = true    const fields = objectType.getFields()    Object.keys(fields).forEach(fieldName => {      const field = fields[fieldName]      const { resolve = defaultFieldResolver } = field      field.resolve = async function (...args) {        // Get the required scopes from the field first, falling back        // to the objectType if no scopes is required by the field:        const requiredScopes = field._requiredAuthScopes || objectType._requiredAuthScopes        if (!requiredScopes) {          return resolve.apply(this, args)        }        const context = args[2]        if (!context.req.user) {          throw new Error('Unauthorized')        }        if (!_.some(context.req.user.permissions, pm => _.includes(requiredScopes, pm))) {          throw new Error('Forbidden')        }        return resolve.apply(this, args)      }    })  }}module.exports = AuthDirective
 |