README
Express Endpoint ACL
In a webapp almost all of the user interaction will go through an endpoint. If you were to have an access control system in place, it can be endpoint based. For now, this module supports MySQL backed express applications.
Overview
You can implement Role Based Access Control using this module. Users can have a set of roles. Roles will have access to certain permissions.
User - Can be identified by an integer or string. (You create & handle users. Module just needs username of some sort)
Role - Has an id & a name with an optional description. (Library handles roles)
Permission - Basically any endpoint is a permission. A permission has id, name, endpoint, method and an optional description (Library creates & handles permissions)
Installation
npm install --save express-endpoint-acl
Run migrate
& update-permission
scripts after installing to create required tables in your db & inserting permissions rows in permission table.
How it works
This library stores roles, permissions, user role mapping & role permission mapping in tables role
, permission
, userRole
, rolePermission
respectively. After npm install a migration runs and creates these tables in your database. Configuration for database connection is obtained from acl.json
. If you didn't have acl.json
when you install the module, migration won't run. But, you can execute a script at later point to run the migration.
As mentioned permissions are endpoints, they are stored in permissions table automatically by a script. Users can be mapped to multiple roles using userRole
table. Roles can have many permissions, rolePermission
table handles that. Permissions are automatically fetched & updated in the table when you run update permission script. However, you have to make userRole
& rolePermission
entries as per your needs. Endpoints are provided with this library to ease the pain. You can build a UI on top of it if you want.
Permissions are always checked against roles. Roles of the incoming request will be determined by the module and it is checked for the permissions it has access to.
After needed rows are inserted into the table, the middleware provided by this library takes care of the access control. If an incoming request doesn't have access to an endpoint, 401
status code is sent as response.
Configuration
Middleware provided by this library has to be used before you define your routes and after the app
object export. Also, you have to make sure username is always available in the userIdField
(know more on this under acl.json
section) of req
object. Minimal example,
const express = require('express');
const { acl } = require('express-endpoint-acl');
const profile = require('./routes/profile');
const inventory = require('./routes/inventory');
const app = express();
// Middlware to set username
app.use((req, res, next) => {
const jwtToken = req.headers.authorization;
jwt.verify(_.split(jwtToken, ' ')[1], 'myjwtsecret', (err, decodedToken) => {
if (token) {
req.username = token && token.mobileNumber; // Setting username in the usernameField
}
return next();
});
});
// Exporting app just before using acl middleware
module.exports = { app };
app.use(acl); //Access control kicks in!
// Routes are defined after using acl middleware
app.use('/', profile);
app.use('/', inventory);
This module works based on the configuration provided by config/acl.json
. Your application's package.json
& config
directory must be inside same directory. Like this,
My Project
|- src
|- config/
|- acl.json
|- node_modules/
|- module1
|- module2
|- express-endpoint-acl
|- test/
|- app.js
|- package.json
|- package-lock.json
acl.json format
Below JSON has every single valid keys to have in acl.json
. Purpose of every JSON key has been explained below.
{
"appPath": "app",
"connectionPath": "./utils/db",
"userIdField": "username",
"includeCheck": [],
"endpointPrefix": "acl",
"excludeCheck": [
"\/public\/.*"
],
"excludedRoleList": [
"Admin"
]
}
appPath (Required)
Express app
object path relative to package.json
. NOTE: You've to export app
object with the name app
.
connectionPath (Required)
MySQL connection
object path relative to package.json
. NOTE: You've to export connection
object with the name connection
.
userIdField (Required)
Key from which username
can be accessed from req
object of express middleware. You have to make it available from req
object. It is fetched from it and the roles of the user is found from userRole
table.
includeCheck
Array of RegEx
strings against which the endpoint (req.path
) is tested and access check is carried out. In case of a clash with excludeCheck, includeCheck
has higher priority and applied.
endpointPrefix
Prefix to add to the acl endpoints. If not provided, defaults to admin
.
excludeCheck
Array of RegEx
strings against which the endpoint (req.path
) is tested and access check is skipped. In case of a clash with includeCheck, includeCheck
takes priority.
excludedRoleList
Array of role names which will have access to all endpoints of the express app.
Scripts
This module comes with 2 bin scripts that can be executed at anytime. You can use npx
, if you have installed or you can execute the bin with full path.
npx express-endpoint-acl migrate
// or
node node_modules/express-endpoint-acl/bin/express-endpoint-acl migrate
Migration
Creates the tables role
, permission
, userRole
, rolePermission
if they don't already exist in the database. If you forgot to add acl.json
before installing, you can run the command mentioned below to create the tables.
npx express-endpoint-acl migrate
Update Permission
Updates the permission table with the endpoints you're using in your express app by running this script. It detects all the endpoins you are using in your app and creates rows in the table.
npx express-endpoint-acl update-permission
Endpoints
Handful of endpoints are provided with this library for making this library more usable. You can either build a UI on top of it or just use the API interface. You can change the admin
prefix by using endpointPrefix
config.
const { acl, aclEndpoint } = require('express-endpoint-acl');
app.use(acl);
app.use('/', aclEndpoint);
NOTE: You can also import and use aclEndpointUtils
- source. This will come in handy, if you want to add something to the response that the default endpoints already provide.
/admin/role
method: GET
Role list, Sample response
{
"roleList": [
{
"id": 1,
"name": "Admin",
"description": "Manages everything"
}
]
}
/admin/permission
method: GET
Permission list, Sample response
{
"permissionList": [
{
"id": 1,
"name": "ROLE_GET",
"endpoint": "/role",
"method": "GET",
"description": null
},
{
"id": 2,
"name": "PERMISSION_GET",
"endpoint": "/permission",
"method": "GET",
"description": null
},
]
}
/admin/role
method: POST
Create/update role, If a role with the name already exists, it updates the description.
Body parameters (x-www-form-urlencoded)
{
name: 'Admin', // mandatory
description: 'Manages everything' // optional
}
Sample response
{
"msg": "Role updated successfully",
"id": 2
}
/admin/permission
method: POST
Create permission, Though permissions represents endpoints, sometimes we can create and use custom permissions. If a permission with the name already exists, it updates the description.
Body parameters (x-www-form-urlencoded)
{
name: 'SHOW_DELIVERY_STATUS', // mandatory
description: 'Access to view delivery status on order page' // optional
}
Sample response
{
"msg": "Permission updated successfully",
"id": 4
}
/admin/role
method: DELETE
Delete role, Body parameters (x-www-form-urlencoded)
{
roleId: 2
}
Sample response
{
"msg": "Role deleted successfully"
}
/admin/userRole
method: GET
Get userRole list, Parameters
Pass anyone of the userId
or roleId
. Both to see if that combination available. You can query multiple user or role id by comma separating.
{
userId: "9999999999",
roleId: "1,2"
}
Sample response
{
"userRoleList": [
{
"userId": "999999999",
"roleId": 1,
"roleName": "Admin",
"roleDescription": "Manages everything"
}
]
}
/admin/rolePermission
method: GET
Get rolePermission list, Parameters
Pass anyone of the roleId
or permissionId
. Both to see if that combination available. You can query multiple role or permission id by comma separating.
{
roleId: "1,2"
permissionId: 1,
}
Sample response
{
"rolePermissionList": [
{
"roleId": 1,
"permissionId": 1,
"permissionName": "REQUESTOTP_POST",
"permissionDescription": null,
"roleName": "Admin",
"roleDescription: "Manages everything",
"endpoint": "/requestOTP",
"method": "POST"
},
{
"roleId": 3,
"permissionId": 1,
"permissionName": "REQUESTOTP_POST",
"permissionDescription": null,
"roleName": "Marketer",
"roleDescription: "Helps product reach more people",
"endpoint": "/requestOTP",
"method": "POST"
}
]
}
/admin/userRole
method: POST
/DELETE
Update/delete userRole, Use POST
method for adding a mapping entry and DELETE
to remove.
Body parameters (x-www-form-urlencoded)
Pass either userId
& roleId
for single entry or userRole
JSON string for multiple entries.
{
userId: '999999999',
roleId: 1,
userRole: [
{
userId: '9999999999',
roleId: 1
},
{
userId: '8888888888',
roleId: 1
}
]
}
Sample response
{
"msg": "User, role mapped succesfully"
}
/admin/rolePermission
method: POST
/DELETE
Update/delete rolePermission, Use POST
method for adding a mapping entry and DELETE
to remove.
Body parameters (x-www-form-urlencoded)
Pass either roleId
& permissionId
for single entry or rolePermission
JSON string for multiple entries.
{
roleId: 1,
permissionId: 5,
rolePermission: [
{
roleId: 1,
permissionId: 1
},
{
roleId: 1,
permissionId: 2
}
]
}
Sample response
{
"msg": "Role, permission mapped succesfully"
}
License
MIT