DENY TRAVERSE on neo4j 4.0 EE

Quick question,

Based on the example of Doctor and Patient of the documentation, how can I prevent Doctor A to query the Patients of Doctor B wih the new DENY TRAVERSE capability introduced in neo4j 4.0 EE?

N.B. Multi-tenancy and sharding are not options as the aggregation is likely to be at the department or organization level.


Currently the RBAC features don't allow granting/denying based upon property values, so until that feature is available you would need a different approach, such as using separate labels on patients based upon their doctor, and then having separate roles per doctor, but that may not be scalable with a large number of doctors.

Provided that each doctor doesn't interact with Neo4j directly, but through an application, you may need to encode some Cypher logic into the queries executed such that it checks that the patient is the patient of the doctor in question (likely provided by a parameter).

Thanks @andrew.bowman

It seems more complicated than I thought. Is it a use case that does not make sense as a graph access control?

It is a more complex case. While we have plans for RBAC based on property values, I don't believe an arbitrary pattern filtering integration with RBAC isn't currently being considered. We can add the idea to the backlog for discussion, however the question will come up for how complex this kind of feature would be allowed to get, and how expensive that may end up being.

For example, your case would likely have to be associated with a label, and be able to accept an arbitrary predicate for evaluation as if it were in a WHERE clause. Something like:

DENY TRAVERSE ON GRAPH healthcare NODES (p:Patient) WHERE (p)-[:PATIENT_OF]->(:Doctor {name:'B'})

But RBAC syntax at this point doesn't have a means of binding nodes (I'm falling back to constraint syntax as possibility), and there's still an open question of how complex we want to allow this to become, and if it can truly capture what we want.

In your example, for instance, I don't think a case of excluding patients from a specific named doctor would be a usual case. I'd think you'd want something more general, such as denying traverse on patients that are not yours. But with just the WHERE clause, there's no mechanism for finding the specific user based on login info via a MATCH, it would have to be hard-coded into the DENY or GRANT command.

So there are a lot of complexities to this and answers that are not clear. Given the amount of work in our queue, I wouldn't anticipate any movement or discussion on this anytime soon.

That said, pattern based evaluations here don't seem out of place for graph access control. It's not a bad request at all, it's just far from being low-hanging fruit.

Ok. I should then consider using Neo4j always through a client or service.

Then, I believe that clients or neo4j-graphql-js type of libraries that can inject these clauses to the cypher statements would be more adequate.

Would have been interesting to have a filtering of the return of a statement with an arbitrary MATCH DENY cypher pattern. I was thinking that TRAVERSE check capability was very similar to my use case as I don’t know the details of the implementation (nor understand maybe :sweat_smile:)

TRAVERSE covers cases of being able to MATCH to nodes, as well as expansion through these nodes/relationships, so it covers more than just what you return.

For a case of a check of the nodes being returned, you might check out triggers in APOC Procedures, but be aware that this doesn't have direct ties to RBAC, and also it won't catch cases where you project and return a property from a node, instead of returning the node itself.

Hi @andrew.bowman & @emre

I would like to try this myself but not able to find doctor patient data as given in section 10.3. Is it already available like Movie data?


Hi Danny, I don't believe so. There's just the data model and examples for the model, no actual graph data.

Thanks Andrew!

I populated it with some graph data. Please share as required

HealthCare database

create (danny:Patient {name:'Danny', SSN:'1001', Address:'Delhi', DOB:'01-01-1975'})
create (shridhar:Patient {name:'Shridhar', SSN:'1002', Address:'Cheenai', DOB:'01-01-1976'})
create (jaspreet:Patient {name:'Jaspreet', SSN:'1003', Address:'Punjab', DOB:'01-01-1977'})
create (shrey:Patient {name:'Shrey', SSN:'1004', Address:'Indor', DOB:'01-01-1978'})
create (deepak:Patient {name:'Deepak', SSN:'1005', Address:'Uttar Pardesh', DOB:'01-01-1979'})

create (dengue:Disease {name:'Dengue', description:'Dengue'})
create (tb:Disease {name:'TB', description:'Tuberculosis'})
create (sf:Disease {name:'Swine flu', description:'Swine flu'})

create (wc:Symptom {name:'Wet Cough', description:'Wet Cough'})
create (jp:Symptom {name:'Joint Pain', description:'Wet Cough'})
create (hf:Symptom {name:'High Fever', description:'Wet Cough'})
create (cup:Symptom {name:'Coughing up blood', description:'Coughing up blood'})
create (uwl:Symptom {name:'Unintentional weight loss', description:'Unintentional weight loss'})
create (f:Symptom {name:'Fatigue', description:'Fatigue'})
create (st:Symptom {name:'Sore throat', description:'Sore throat'})
create (wre:Symptom {name:'Watery, red eyes', description:'Watery, red eyes'})
create (bch:Symptom {name:'Body aches Headache', description:'Body aches Headache'})

create (wc)-[:OF {probability:'2'}]->(dengue)
create (jp)-[:OF {probability:'3'}]->(dengue)
create (hf)-[:OF {probability:'3'}]->(dengue)

create (cup)-[:OF {probability:'2'}]->(tb)
create (uwl)-[:OF {probability:'3'}]->(tb)
create (f)-[:OF {probability:'1'}]->(tb)

create (st)-[:OF {probability:'1'}]->(sf)
create (wre)-[:OF {probability:'3'}]->(sf)
create (bch)-[:OF {probability:'2'}]->(sf)

Create (danny)-[:HAS {date:'01-01-2020'}]->(wc)
Create (danny)-[:HAS {date:'01-01-2020'}]->(jp)
Create (danny)-[:HAS {date:'01-01-2020'}]->(hf)

Create (jaspreet)-[:HAS {date:'01-01-2020'}]->(st)
Create (jaspreet)-[:HAS {date:'01-01-2020'}]->(wre)
Create (jaspreet)-[:HAS {date:'01-01-2020'}]->(bch)

Create (danny)-[:DIAGNOSIS]->(dengue)
Create (jaspreet)-[:DIAGNOSIS]->(sf)

Doctors(Alice) who need to perform diagnosis on patients.
(Should be able to read and write most of the graph. We would, however, like to prevent the doctor from reading the patient’s address.)
Nurses(Daniel) who need to treat patients.
(The nurse should be able to perform all tasks that both the doctor and the receptionist can do. For this reason, we do not need to create a dedicated role, but can assign nurses to both doctor and receptionist roles.)
Receptionists(Bob) who need to identify and record patient information.
(Should be able to read and write all patient data, but not be able to see the symptoms, diseases or diagnoses.)
Researchers(Charlie) who need to perform statistical analysis of medical data.
(Should be able to perform statistical analysis on all data, except patients’ personal information, and as such should not be able to read most patient properties.)
IT administrators(Tina) who need to administer the database, creating and assigning users.
(Restrict access to the patients SSN. To achieve this, we can create this role by copying the built-in admin role and modifying the privileges of the copy)

1 Like