Role Based Access Control, Serverless, proof of concept
Role Based Access Control proof of concept / experiment, based on Casbin, AWS Lambda and DynamoDB
Table of Contents
Goals
Many applications end up reinventing wheels by building tightly coupled permissions that rot and become difficult to change. The aims of this poc are:
- Evaluate whether a serverless microservice api can be used to serve multiple client systems (backend API and FE UI).
- Re-use of the Casbin engine.
- Later: Adding a react page to manage the permissions.
Scenario
The set of users & groups policies is intended to provide a reasonably sane model with realistic volume of data.
Groups
Each of the 4 groups "owns" 2 obj
ects that can be act
ed on. Each obj
ect can be act
ioned following the CRUD model. (create
, read
, update
, delete
).
100 users were randomly generated and assigned to a group.
To validate that users can be assigned to multiple groups, the user, donella.bilbrey@legalandfinance.com
belongs to both the legal
and finance
groups.
- sales, 26 users.
- monthly_sales_report
- annual_sales_report
- development, 28 users.
- release_to_staging
- release_to_production
- finance, 30 users.
- monthly_accounts
- annual_accounts
- legal, 16 users.
- sales_contract
- employment_contract
Getting Started
You will need an AWS account with access keys configured. I have access to short term AWS accounts so can safely export them on the command line.
Software Used
- Node LTS, See:
.nvmrc
- Serverless framework, See:
package.json
- Docker
- Docker Compose Optional - for local development.
Configuration Steps
-
Clone this repo
-
Copy
.env.sample
to.env
and complete -
Install dependencies
npm install
Local development
npm run dev
This uses serverless offline
and docker-compose to provide:
- The api running on localhost:3000
- A DynamoDB admin GUI at localhost:8001.
- A local DynamoDB instance with the table created.
-
First time – seed the initial Casbin data into the database by visiting:
http://localhost:3000/seed
Deployment
Export AWS Credentials (Optional)
If not already configured. Note: The leading space is intentional
export AWS_ACCESS_KEY_ID=<your-key-here> && export AWS_SECRET_ACCESS_KEY=<your-secret-key-here>
Deploy
serverless deploy
Invocation
After successful deployment, the serverless output will contain an endpoint. Take note of this and replace <your-endpoint-here>
in subsequent commands.
...
endpoints:
ANY - https://xxxxxx.execute-api.us-east-1.amazonaws.com
...
Thunder Client (VS Code Extension)
A suite of example api calls, with simple tests, are provided using the Thunder client VS Code plugin. The definitions are stored in the thunder-tests directory.
Note: Configure the thunder client extension to "Load From Project". After enabling this setting, you may need to restart VS to load the collection.
Api calls can be directed to local or remote endpoints by changing the Env
=> vars
=> SERVER_ENDPOINT
value.
Tip: A useful sanity check is to use the "Run All" functionality of the permissions collection.
Endpoints
The implementation is rbac_with_resource_roles
with the model defined in a static file. The role g2
is currently unused.
Policies are loaded via the /seed
endpoint. See rbac_resource_roles_policy.json for the definition of the policies used in the code.
Route | HTTP Method | Description |
---|---|---|
/seed |
GET | Development convenience method to load the sample policies into dynamoDB. Clears the data and reloads. |
/enforce/:sub/:obj/:act |
GET | Tests a subject, object, action against the seeded policies. Hint :sub can be a user OR group. |
/rolesfor/:sub |
GET | Returns the roles defined for a subject. Wraps Casbin's RBAC getRolesForUser Hint: try donella.bilbrey@legalandfinance.com |
/implicitrolesfor/:sub |
GET | Returns the roles defined for a subject. Wraps Casbin's RBAC getImplicitRolesForUser Hint: try donella.bilbrey@legalandfinance.com |
/permissionsfor/:sub |
GET | Returns the permissions defined for a subject. Wraps Casbin's getPermissionsForUser Hint: This method requires a group to be passed for results as users perms are via groups. |
/implicitpermissionsfor/:sub |
GET | Returns the permissions defined for a subject. Wraps Casbin's getImplicitPermissionsForUser Note: Potentially useful to proxy (via an API) to a front end UI |
Example call:
curl <your-endpoint-here>/enforce/angus.muldoon@development.com/release_to_staging/create
Which will respond with:
{
"sub": "angus.muldoon@development.com",
"obj": "release_to_staging",
"act": "create",
"result": true
}
Group OR user level enforcement
The /enforce/:sub/:obj/:act
endpoint can be queried with a :sub
of a user or group. EG:
curl <your-endpoint-here>/enforce/angus.muldoon@development.com/release_to_staging/create
curl <your-endpoint-here>/enforce/development/release_to_staging/create
Discussion
Why node and express?
Casbin is implemented in a number of languages. Go appears to be the best supported and "native" language. node.js was selected as it is widely supported with plugins, easily understood and cold startup times in lambda are fast.
The aim is to be a Casbin "wrapper", therefore little custom code is needed.
Why DynamoDB?
DynamoDB is serverless and fully managed. The maintenance overhead is low.
Typically permissions are a read heavy workload. If performance becomes an issue then DAX should be evaluated.
Alternatively Casbin has support for a wide range of backend storage adaptors so the implementation can easily be changed depending on throughput requirements.
Lambda Cost Control and Performance
The lambda is currently configured to use the x86_64
architecture.
There is potential for significant cost savings, while increasing performance, by changing the architecture to arm64
. More information can be found at the AWS blog