Role Based Access Control in Neo4j 4.1

Adam Cowley
Neo4j Developer Blog
7 min readJun 23, 2020

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:

The Northwind Data Model in Neo4j

A :Customer :PURCHASED an :Order which contains one or more :Products. Those :Products 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 properties
  • ON GRAPH northwind — To the database ‘northwind’
  • NODES Product, Category — To nodes with the labels Product or Category
  • TO 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.

A list of the website user’s roles inherited from 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

Further Reading

--

--