Socket
Socket
Sign inDemoInstall

@knotel/authorize

Package Overview
Dependencies
48
Maintainers
25
Versions
22
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    @knotel/authorize

Opinionated authorization middleware for federated GraphQL services.


Version published
Maintainers
25
Install size
3.63 MB
Created

Readme

Source

Getting Started

Import this package into any Apollo federated service within the mono infrastructure where the User <> Profile association exists.

@knotel/authorize makes a few assumptions:

  • The federated service has Profile and Role tables.
  • The Role table has a policy column containing a valid CSV format string.
  • The policy CSV string is formatted in the following syntax:
p, resource, action

The node-casbin depedency is normally case-sensitive and requires comma-separated values to be padded with whitespace. However, the isAuthorized method is written to be more flexible. So case and space should not matter.

Installation

yarn add @knotel/authorize

Usage

app/graphql/permissions/index.js

For more information on how to construct your permissions directory, see the official graphql-shield documentation.

const { permissions, isAuthorized } = require('@knotel/authorize')

// permissions is a graphql-shield function (see shield)
module.exports = permissions({
    Query: {
        Resource: isAuthorized('Read'),
        allResources: isAuthorized('List'),
    },
    Mutation: {
        createResource: isAuthorized('Create'),
        updateResource: isAuthorized('Edit'),
        deleteResource: isAuthorized('Delete'),
    },
})

app/graphql/schema.js

Nothing functional is going on here. Just imported and exported through this file for convenience and cleaner imports.

const typeDefFiles = require('./typedefs')
const resolvers = require('./resolvers')
const permissions = require('./permissions')

const typeDefString = typeDefFiles.join('\n')
const typeDefs = gql(typeDefString)

module.exports = {
  typeDefs,
  resolvers,
  permissions,
}

app/app.js Import the permissions dependency and apply it as middleware to your GraphQL schema. Then, in your global context object, add the logic to derive the policy document for a given user and inject it into the context object.

isAuthorized checks for the scopes specified in your permissions directory in the policy document provided by the context object.

const { applyMiddleware } = require('graphql-middleware')

const { typeDefs, resolvers, permissions } = require('./graphql/schema')

const schema = buildFederatedSchema([{
    typeDefs,
    resolvers
}])

const server = new ApolloServer({
    schema: applyMiddleware(schema, permissions),
    context: ({ req, context }) => {
        const profile = await Profile.findOne({
            where: {
                userId: req.user.id
            }
        })
        const role = await profile.getRole({ raw: true })
        return { user: req.user, policy: role.policy, req: req }
    },
})

Creation of Policy Documents

Ideally, the policy document should be stored on the role record belonging to the user. Not the user or profile itself.

That being said, the injection of the policy property into the global context object offers you the flexibility to derive the policy document from wherever you choose.

app/db/seeders/*.js

To create your policy documents, this package also includes a small utility to use in the creation of your seed Role records.

const { generatePolicy } = require('@knotel/authorize')

const resourcePermissions = [{
    name: 'Space',
    resource: ['List', 'Read', 'Create', 'Edit', 'Delete']
}]

module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.sequelize.query(`
      UPDATE "Role" SET "policy" = '${generatePolicy(resourcePermissions)}' WHERE "Role"."name" = 'ACCOUNT_EXECUTIVE';
    `)
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.sequelize.query(`
      UPDATE "Role" SET "policy" = NULL WHERE "Role"."name" = 'ACCOUNT_EXECUTIVE';
    `)
  },
}

Validation of Policy Documents

An important aspect of ensuring a bug-free authorization framework is persisting policy documents in the format expected by the @knotel/authorize.

const { isValidPolicy } = require('@knotel/authorize')

const role = Role.findByPk(req.params.id)
const isValid = await isValidPolicy(role.policy)

Resource Authorization

PrefixQuery/Mutation ExampleExample ArgsScopeDescription
allallResources()list"I want all resources without knowing IDs."
ownallResources()ownlist"I want all my/our resources without knowing IDs."
anyResource(id)read"I want any resource assuming I know the ID."
ownResource(id)ownread"I want my resource assuming I know the ID."
anydeleteResource(id)delete"I want my resource assuming I know the ID."
owndeleteResource(id)owndelete"I want to delete my resource assuming I know the ID."
anyupdateResource(id, params)edit"I want to update any resource assuming I know the ID."
ownupdateResource(id, params)ownedit"I want to update my resource assuming I know the ID."
newcreateResource(params)create"I want to create a new resource."

FAQs

Last updated on 26 Oct 2019

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc