Role Based Access Control in Neo4j 4.1
The GA of Neo4j 4.1 is officially out this week. Head over to neo4j.com/download or create a new local graph in Neo4j Desktop to give it a try now.
In celebration, I thought I’d write a quick tutorial on the new Role Based Access Control features.
A brief history
Role Based Access Control has been around since Neo4j version 3.1 with built in reader, publisher, architect and admin roles. A new set of DBMS procedures allowed you to create users and assign them to roles.
Along with multiple databases, Version 4.0 introduced a new syntax for defining roles. Instead of calling procedures, these commands are now run against the system
database. For example, you can use the CREATE USER
to create a User before running GRANT ROLE
to assign the user to a role.
:use systemCREATE USER adam SET PASSWORD $password;
GRANT ROLE reader to adam;
Note: Neo4j 4.1 is now clever enough to recognise system commands, so the :use system
command that was required in 4.0 is no longer necessary.
A worked example
The official documentation provides an example based around healthcare but I thought I’d try to put a different spin on it.
Instead, I’ll take a look at how these features can be applied to the Northwind dataset. If you’re not familiar, Northwind is an example dataset containing a Product Inventory including Products, Categories and Suppliers; Customers and their Orders; and Employee information and their territories.
For more information or to import the data yourself, either check out the Relational Data to Neo4j developer guide or run the Northwind Browser Guide in Neo4j Browser:
:play northwind
There is the how the data model looks once the data is in Neo4j:
A :Customer
:PURCHASED
an :Order
which contains one or more :Product
s. Those :Product
s are a part of a :Category
and supplied by a :Supplier
.
Let’s assume that the data has all been loaded into a database called northwind
.
Sensitive Information
If a Customer’s information, or their order history fell into the wrong hands, this would be a GDPR nightmare. For this reason, we should create some constraints up front for the different kinds of users that will be accessing the data.
In a real world scenario, there may be many types of users that would need to access this data. For the sake of brevity, let’s settle on the following users and their purpose for accessing the data:
- The website should be able to read all data from the website, create new orders and customers, but have readonly access to Products, Categories and Suppliers.
- Suppliers should be able to amend Products and their Category listings.
- The Data Science team should be able to view Product and Order information without accessing the Customer details.
Let’s start with a user that will be used by the website’s application servers. For this, I’ll open up cypher-shell, switch to the system database, and run the CREATE USER
command.
neo4j@neo4j> CREATE USER website SET PASSWORD 'letmein' CHANGE NOT REQUIRED;
If I run theSHOW USER
command, I can see that the user has been automatically granted access to the DEFAULT
graph (neo4j unless you set dbms.default_database
in neo4j.conf) with this role of PUBLIC.
neo4j@neo4j> SHOW USER website PRIVILEGES;
+-----------------------------------------------------------------------------------+
| access | action | resource | graph | segment | role | user |
+-----------------------------------------------------------------------------------+
| "GRANTED" | "access" | "database" | "DEFAULT" | "database" | "PUBLIC" | "website" |
+-----------------------------------------------------------------------------------+
The new PUBLIC Role
The PUBLIC
role is a new feature of 4.1, and allows you to set default the default permissions for all users by default. This role gets assigned to all users by default.
Important: This role is inherited by every database by default. It can be modified but can’t be removed.
So, if the information is stored in the northwind database, we can use this role to grant all users access to the database.
GRANT ACCESS ON DATABASE northwind TO PUBLIC;
Although this grants access to the database, it doesn’t currently give the user the privileges to read any of the data that it contains. For that, we will need to be explicit. All roles should be able to read Products and Categories, so we can grant the ability to runMATCH
queries on the database.
GRANT MATCH {*} ON GRAPH northwind NODES Product, Category TO PUBLIC;
Let’s take a second to unpack that:
GRANT MATCH {*}
— Allow the user access to all propertiesON GRAPH northwind
— To the database ‘northwind’NODES Product, Category
— To nodes with the labels Product or CategoryTO PUBLIC
— To the role ‘PUBLIC’
Now all users will be able to run a MATCH
query on Product or Category nodes but no relationships will be visible to the User. For this, we have to GRANT TRAVERSE
privileges.
GRANT TRAVERSE ON GRAPH northwind RELATIONSHIPS PART_OF to PUBLIC
Now all users will be able to read the product catalogue.
Restricting Writes
Another feature introduced in 4.1 is the ability to create finer-grained control for write queries. Now it is also possible to restrict what a user can write to the database.
In our case, the website user that we’ve created should be able to create new Customer accounts and Orders but shouldn’t be able to modify the product catalogue. By default, the PUBLIC
user doesn’t the ability to create nodes, so if they try to create something they will see the following error:
Neo.ClientError.Security.Forbidden
Create node with labels 'Category' is not allowed for user 'website' with roles [PUBLIC].
Instead of assigning this to the PUBLIC
role, we should instead create a new role and then GRANT
the role to the user.
CREATE ROLE websiteUser;
GRANT ROLE websiteUser TO website;
Now we can be specific about what the user can do. In this case, we want to allow them to view all properties on the Customer
and Order
nodes, and also traverse the PURCHASED
and ORDERS
relationships.
GRANT MATCH {*} ON GRAPH northwind
NODES Customer, Order
TO websiteUser;GRANT TRAVERSE ON GRAPH northwind
RELATIONSHIPS PURCHASED, ORDERS
TO websiteUser;
A quick look at the user’s privileges by running SHOW USER website PRIVILEGES
should show the privileges that have been inherited from both the PUBLIC and websiteUser roles.
Now the website user should be able to see the all of the information held about the order.
Now that the user can read the data, we want to make sure that they can write data as well.
There are a number of properties against the node that the user should be able to write, but also a few that shouldn’t be available. Let’s say for arguments sake, that a back-office process runs which assigns an employee, then another system that sets the shippedDate once the order is dispatched.
{
"shipCity": "Aachen",
"orderID": "11067",
"freight": "7.98",
"requiredDate": "1998-05-18 00:00:00.000",
"employeeID": "1",
"shipPostalCode": "52066",
"shipName": "Drachenblut Delikatessen",
"shipCountry": "Germany",
"shipAddress": "Walserweg 21",
"shipVia": "2",
"customerID": "DRACD",
"shippedDate": "1998-05-06 00:00:00.000",
"orderDate": "1998-05-04 00:00:00.000",
"shipRegion": "NULL"
}
We could be explicit and define the properties that the role should be able to apply by providing a comma separated list. This may cause problems if a new feature is added to the website. Instead, we can use the wildcard *
character to allow the user to set all properties, then be descriptive about what we want to disallow.
First, let’s give the role permission to CREATE
a node:
GRANT CREATE ON GRAPH northwind NODES Order TO websiteUser;
Next, allow the role permission to SET
all properties using the wildcard:
GRANT SET PROPERTY {*} ON GRAPH northwind
NODES Order
TO websiteUser;
Then, deny permission to set the employeeId
and shippedDate
roles:
DENY SET PROPERTY {employeeID, shippedDate} ON GRAPH northwind
NODES Order
TO websiteUser;
Once this has been applied, any user with this role will receive an error:
website@northwind> CREATE (:Order {employeeID: 1})Neo.ClientError.Security.Forbidden
Set property for property 'employeeID' is not allowed for user 'website' with roles [PUBLIC, websiteUser].
Hopefully by now you get the idea. The combination of GRANT
and DENY
commands allow you to be very specific or generic when assigning roles.
What else is new in 4.1
Along with new writer privileges, Neo4j 4.1 now also offers commands for granting or denying privileges held by the built in roles that were introduced in previous 3.x versions.
Reader — Read-only access to the database
GRANT ACCESS ON DATABASE * TO reader
GRANT MATCH {*} ON GRAPH * TO reader
Editor — Read access plus limited ability to write to the graph using the existing labels, relationship types, and property keys.
GRANT ACCESS ON DATABASE * TO editor
GRANT ALL GRAPH PRIVILEGES ON GRAPH * TO editor
Publisher — Read and write access to the graph with the ability to create new labels, relationship types, and property keys.
GRANT ACCESS ON DATABASE * TO publisher
GRANT NAME MANAGEMENT ON DATABASE * TO publisher
GRANT ALL GRAPH PRIVILEGES ON GRAPH * TO publisher
Architect —As above but with the added ability to create indexes and constraints.
GRANT ACCESS ON DATABASE * TO architect
GRANT NAME MANAGEMENT ON DATABASE * TO architect
GRANT INDEX MANAGEMENT ON DATABASE * TO architect
GRANT CONSTRAINT MANAGEMENT ON DATABASE * TO architect
GRANT ALL GRAPH PRIVILEGES ON GRAPH * TO architect
Admin — Full privileges to the database: reading, writing, creating indexes and constraints, creating databases and users, and managing the transactions of others.
GRANT ALL DBMS PRIVILEGES ON DBMS * TO admin
GRANT ALL DATABASE PRIVILEGES ON DATABASE * TO admin
GRANT ALL GRAPH PRIVILEGES ON GRAPH * TO admin
GRANT TRANSACTION MANAGEMENT ON DATABASE * TO admin