graphql-acl-service
v2.0.1
Published
GraphQL authorization made easy
Downloads
16
Readme
graphql-acl-service
GraphQL authorization made easy
Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
Prerequisites
Install it via npm
npm install graphql-acl-service
Installing
A step by step series of examples that tell you how to get a development env running
Say what the step will be
git clone https://gitlab.com/gotoar/graphql-acl-service.git
npm install
You can find an example in the examples folder, or a boilerplate at this repo
Usage
Concepts
- Rule: think of them as barriers. If the condition is met, the user shall not pass
- Methods: list of methods to be exposed via the service
- Fields: The item's elements to be protected with rules
- Exception: Every time a request is blocked by a rule an exception is thrown
- Role: This plugin is intended to be exposed to users, so the role refers to the access level they have
Basic service structure
Let's see an example of a configuration object and then explain what each thing does
const Service = require('graphql-acl-service')
const controller = require('./route/to/controller')
const CustomException = require('./route/to/custom/exception')
const exampleConfig = {
exception: Error,
is: {
role_field: 'role',
privileged: ['super', 'admin']
},
protected: ['id'],
resolvers: {
yourself: async (is, user, raw) => is.Role('admin') && user.uid !== raw.customer_id
},
methods: [
{
name: 'batchFetch',
method: controller.success
},
{
name: 'create',
method: controller.create,
rules: [
{ type: 'privileged', message: 'Only admins' },
{ type: 'yourself', message: 'You can only create users for yourself', exception: CustomException }
],
sanitize: true
}
],
fields: {
invite: [
{ type: 'privileged', message: 'Only admins' }
],
users: [
{ type: 'privileged', message: 'Only admins' }
]
}
}
const ExampleService = new Service({ role: 'super' }, exampleConfig)
Exception
As explained in the concepts section, when a rule returns true an exception is thrown. If the rule has an exception set, it will throw that one, but if there isn't, it will default to the one defined in the root of the config object
IS
is provides helper methods that allow us to check whether the user is privileged or not, or if the role equals a value. In order to do that we need to declare the role field and the values that a privileged user will have. If we want to check for a specific role we can do so creating a resolver like this:
{
...
only_super: async (is, user, raw) => !is.Role('super'),
...
}
Protected
Maybe we don't want our users populating some data, eg.: an autogenerated id. So we can define those inside an array and those fields will be stripped from the payload
Resolvers
If you want to protect a method with a custom rule first you have to define it inside resolvers. This will allow to use the same rule to protect many methods while not repeating code. From the resolver you can access everything in the scope of the file and four parameters passed to the function in the following order:
- is: The helper methods that allow us to check for the user role
- user: The whole user data passed when the service is created
- request: The raw request payload
- item: If being used to protect a field, the field data will be passed in this parameter
Methods
An array of objects containing the method resolver, rules and method related configuration. The method configuration object consists of the following fields (Some are required and some are optional)
- name - string - required : The name the method will be exposed with (string)
- method - function - required : Resolver for that method (function)
- rules - array<object> : Rules to check before executing the method. They consist of:
- type: Name of the resolver to use
- message: Error message that will be passed to the exception if the rule returns true
- exception: Custom exception to throw
[ ... { type: 'privileged', message: 'You need to be an admin or a superuser', exception: Error }, ... ]
- sanitize - boolean : Whether or not we want to filter the input with the protected fields defined in the root
- args - array<string> : If the methods needs the data to be passed in a specific order, like this one
You can set it in the args aray like thisconst method = (arg1, arg2) => console.log(arg1, arg2)
And when the method is called with a payload like this{ ... args: ['id', 'name'], ... }
The method will be called this way{ id: '1', name: 'tom', age: '21' }
If you want to pass an arg and the whole payload you can do so by setting the arg with the wildcard *method('1', 'tom')
{ ... args: ['id', '*'], ... }
Fields
An object setting the rules to run for that field
Running the tests
npm run test
And coding style tests
You can check linting with this command
npm run lint
Dependencies
No dependencies
Contributing
Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting merge requests to us.
Versioning
We use SemVer for versioning. For the versions available, see the tags on this repository.
Authors
- Tomas Gorkin - Initial work
See also the list of contributors who participated in this project.
License
This project is licensed under the MIT License - see the LICENSE.md file for details
Acknowledgments
- Hat tip to anyone whose code was used
- Inspiration
- etc