[フレーム]
Skip to content

Fine-grained access control
Enterprise Edition

When creating a database, administrators may want to establish which users can access certain information.

As described in Built-in roles and privileges, Neo4j already offers preset roles configured to specific permissions (i.e. read, edit, or write). While these built-in roles cover many common daily scenarios, it is also possible to create custom roles for specific needs.

This tutorial walks you through a healthcare use case that illustrates various aspects of security and fine-grained access control.

Healthcare use case

To demonstrate the application of these tools, consider an example of a healthcare database which could be relevant in a medical clinic or hospital.

For simplicity reasons, only three labels are used to represent the following entities:

(:Patient)

Patients that visit the clinic because they have some symptoms. Information specific to patients can be captured in properties:

  • name

  • ssn

  • address

  • dateOfBirth

(:Symptom)

A set of symptoms found in a catalog of known illnesses. They can be described using the properties:

  • name

  • description

(:Disease)

Known illnesses mapped in a catalog found in the database. They can be described using the properties:

  • name

  • description

These entities are modelled as nodes, and connected by relationships of the following types:

(:Patient)-[:HAS]→(:Symptom)

When a patient reports to the clinic, they describe their symptoms to the nurse or the doctor. The nurse or doctor then enters this information into the database in the form of connections between the patient node and a graph of known symptoms. Possible properties of interest on this relationship could be:

  • date - date when symptom was reported.

(:Symptom)-[:OF]→(:Disease)

Symptoms are a subgraph in the graph of known diseases. The relationship between a symptom and a disease can include a probability factor for how likely or common it is for people with that disease to express that symptom. This will make it easier for the doctor to make a diagnosis using statistical queries.

  • probability - probability of symptom matching disease.

(:Patient)-[:DIAGNOSIS]→(:Disease)

The doctor can use the graph of diseases and their symptoms to perform an initial investigation into the most likely diseases to match the patient. Based on this, and their own assessment of the patient, the doctor may make a diagnosis which they would persist to the graph through the addition of this relationship with appropriate properties:

  • by: doctor’s name

  • date: date of diagnosis

  • description: additional doctors' notes

security example
Figure 1. Healthcare use case

This same database would be used by a number of different users, each with different access needs:

  • Doctors who need to diagnose patients.

  • Nurses who need to treat patients.

  • Receptionists who need to identify and record patient information.

  • Researchers who need to perform statistical analysis of medical data.

  • IT administrators who need to manage the database, to create and assign users for example.

To create the database, start by creating the nodes and relationships that represent the entities described above.

Create the healthcare database

The following steps assume that you have installed Neo4j Enterprise edition and it is running. For details, see Installation.

  1. Using Cypher Shell, log into the system database as the neo4j user:

    cypher-shell -u neo4j -p my-password -d system
  2. Create the healthcare database:

    CREATE DATABASE healthcare;
  3. Set the healthcare database as the default database:

    CALL dbms.setDefaultDatabase("healthcare");
    +----------------------------------------------------------------------+
    | result |
    +----------------------------------------------------------------------+
    | "Old default database unset. New default database set to healthcare" |
    +----------------------------------------------------------------------+
    1 row
    ready to start consuming query after 9 ms, results consumed after another 4 ms

Import data into the healthcare database

Create the nodes and relationships that represent the entities described above in section Healthcare use case.

  1. Switch to the healthcare database:

    :use healthcare;
  2. Create some data for symptoms:

    WITH ['Itchy','Scratchy','Sore','Swollen','Red','Inflamed','Angry','Sad','Pale','Dizzy'] AS symptoms
     UNWIND symptoms AS symptom
     MERGE (s:Symptom {name:symptom})
     ON CREATE SET s.description = 'Looks ' + toLower(symptom)
     RETURN s.name, s.description;
    Result
    +-------------------------------+
    | s.name | s.description |
    +-------------------------------+
    | "Itchy" | "Looks itchy" |
    | "Scratchy" | "Looks scratchy" |
    | "Sore" | "Looks sore" |
    | "Swollen" | "Looks swollen" |
    | "Red" | "Looks red" |
    | "Inflamed" | "Looks inflamed" |
    | "Angry" | "Looks angry" |
    | "Sad" | "Looks sad" |
    | "Pale" | "Looks pale" |
    | "Dizzy" | "Looks dizzy" |
    +-------------------------------+
    10 rows
    ready to start consuming query after 53 ms, results consumed after another 24 ms
    Added 10 nodes, Set 20 properties, Added 10 labels
  3. Create some data for diseases:

    WITH
     ['Argitis','Whatitis','Otheritis','Someitis','Placeboitis','Yellowitis'] AS diseases,
     ['Chronic','Acute'] AS severity
    UNWIND diseases AS disease
    UNWIND severity as sev
    MERGE (d:Disease {name:sev+' '+disease})
    ON CREATE SET d.description = sev + ' ' + toLower(disease)
    RETURN d.name, d.description;
    Result
    +-----------------------------------------------+
    | d.name | d.description |
    +-----------------------------------------------+
    | "Chronic Argitis" | "Chronic argitis" |
    | "Acute Argitis" | "Acute argitis" |
    | "Chronic Whatitis" | "Chronic whatitis" |
    | "Acute Whatitis" | "Acute whatitis" |
    | "Chronic Otheritis" | "Chronic otheritis" |
    | "Acute Otheritis" | "Acute otheritis" |
    | "Chronic Someitis" | "Chronic someitis" |
    | "Acute Someitis" | "Acute someitis" |
    | "Chronic Placeboitis" | "Chronic placeboitis" |
    | "Acute Placeboitis" | "Acute placeboitis" |
    | "Chronic Yellowitis" | "Chronic yellowitis" |
    | "Acute Yellowitis" | "Acute yellowitis" |
    +-----------------------------------------------+
    12 rows
    ready to start consuming query after 56 ms, results consumed after another 7 ms
    Added 12 nodes, Set 24 properties, Added 12 labels
  4. Create relationships between symptoms and diseases:

    MATCH (s:Symptom) WITH collect(s) as symptoms
    WITH symptoms, size(symptoms) / 2 as maxsym
    MATCH (d:Disease)
    UNWIND range(0,maxsym) as symi
    WITH d, symi, symptoms, toInteger(size(symptoms) * rand()) as si, rand()/2 + 0.5 AS prob
    WITH d, symptoms[si] AS s, prob
    MERGE (s)-[o:OF]->(d)
    ON CREATE SET o.probability = prob
    RETURN d.name, o.probability, s.name;
    Result
    +---------------------------------------------------------+
    | d.name | o.probability | s.name |
    +---------------------------------------------------------+
    | "Chronic Argitis" | 0.5488344602870381 | "Scratchy" |
    | "Chronic Argitis" | 0.660404649462915 | "Itchy" |
    | "Chronic Argitis" | 0.6905998399032373 | "Angry" |
    | "Chronic Argitis" | 0.660404649462915 | "Itchy" |
    | "Chronic Argitis" | 0.8740581222813869 | "Red" |
    | "Chronic Argitis" | 0.7456909803542418 | "Sore" |
    | "Acute Argitis" | 0.607200508350778 | "Pale" |
    | "Acute Argitis" | 0.5772236253537283 | "Red" |
    | "Acute Argitis" | 0.7268375663608245 | "Inflamed" |
    | "Acute Argitis" | 0.847011132303783 | "Itchy" |
    | "Acute Argitis" | 0.8025327549974599 | "Sore" |
    | "Acute Argitis" | 0.5772236253537283 | "Red" |
    | "Chronic Whatitis" | 0.9185112224896539 | "Sore" |
    | "Chronic Whatitis" | 0.8220811592705012 | "Dizzy" |
    | "Chronic Whatitis" | 0.8220811592705012 | "Dizzy" |
    | "Chronic Whatitis" | 0.9947532896439784 | "Scratchy" |
    | "Chronic Whatitis" | 0.5479749642339755 | "Red" |
    | "Chronic Whatitis" | 0.9466973516593605 | "Inflamed" |
    | "Acute Whatitis" | 0.7217509679510017 | "Inflamed" |
    | "Acute Whatitis" | 0.7217509679510017 | "Inflamed" |
    | "Acute Whatitis" | 0.7073350047270233 | "Scratchy" |
    | "Acute Whatitis" | 0.7217509679510017 | "Inflamed" |
    | "Acute Whatitis" | 0.6800748332507602 | "Red" |
    | "Acute Whatitis" | 0.6953854679660172 | "Itchy" |
    | "Chronic Otheritis" | 0.5570795327063996 | "Scratchy" |
    | "Chronic Otheritis" | 0.7615506655612736 | "Swollen" |
    | "Chronic Otheritis" | 0.7147549568270981 | "Angry" |
    | "Chronic Otheritis" | 0.9309059023795485 | "Red" |
    | "Chronic Otheritis" | 0.8339105187862091 | "Dizzy" |
    | "Chronic Otheritis" | 0.7147549568270981 | "Angry" |
    | "Acute Otheritis" | 0.7449502448640619 | "Red" |
    | "Acute Otheritis" | 0.6635390850482914 | "Sad" |
    | "Acute Otheritis" | 0.6488764428922569 | "Itchy" |
    | "Acute Otheritis" | 0.7642990617862074 | "Pale" |
    | "Acute Otheritis" | 0.5532690807468361 | "Scratchy" |
    | "Acute Otheritis" | 0.8062425062999423 | "Inflamed" |
    | "Chronic Someitis" | 0.580678012588533 | "Sore" |
    | "Chronic Someitis" | 0.9569035040624002 | "Red" |
    | "Chronic Someitis" | 0.9328323008783481 | "Inflamed" |
    | "Chronic Someitis" | 0.9569035040624002 | "Red" |
    | "Chronic Someitis" | 0.5492540886308123 | "Pale" |
    | "Chronic Someitis" | 0.9204301026117075 | "Swollen" |
    | "Acute Someitis" | 0.9969140989164824 | "Itchy" |
    | "Acute Someitis" | 0.8756876989165112 | "Swollen" |
    | "Acute Someitis" | 0.9969140989164824 | "Itchy" |
    | "Acute Someitis" | 0.6258855371986936 | "Red" |
    | "Acute Someitis" | 0.9928922186427123 | "Angry" |
    | "Acute Someitis" | 0.6258855371986936 | "Red" |
    | "Chronic Placeboitis" | 0.9837947935707738 | "Itchy" |
    | "Chronic Placeboitis" | 0.7795050137703664 | "Inflamed" |
    | "Chronic Placeboitis" | 0.680595344835278 | "Sad" |
    | "Chronic Placeboitis" | 0.8383237671521345 | "Scratchy" |
    | "Chronic Placeboitis" | 0.7054054618102132 | "Swollen" |
    | "Chronic Placeboitis" | 0.7795050137703664 | "Inflamed" |
    | "Acute Placeboitis" | 0.768802727874529 | "Dizzy" |
    | "Acute Placeboitis" | 0.6645530219645431 | "Scratchy" |
    | "Acute Placeboitis" | 0.9192262998770437 | "Pale" |
    | "Acute Placeboitis" | 0.7321327463249545 | "Itchy" |
    | "Acute Placeboitis" | 0.5768920173860386 | "Sad" |
    | "Acute Placeboitis" | 0.5467367430608921 | "Sore" |
    | "Chronic Yellowitis" | 0.657149882924074 | "Dizzy" |
    | "Chronic Yellowitis" | 0.5274096280530778 | "Swollen" |
    | "Chronic Yellowitis" | 0.657149882924074 | "Dizzy" |
    | "Chronic Yellowitis" | 0.9011165844619397 | "Scratchy" |
    | "Chronic Yellowitis" | 0.5274096280530778 | "Swollen" |
    | "Chronic Yellowitis" | 0.7267736062002124 | "Sore" |
    | "Acute Yellowitis" | 0.7764355480097833 | "Swollen" |
    | "Acute Yellowitis" | 0.9776709262803641 | "Inflamed" |
    | "Acute Yellowitis" | 0.6495454012653183 | "Red" |
    | "Acute Yellowitis" | 0.7764355480097833 | "Swollen" |
    | "Acute Yellowitis" | 0.7395280933743617 | "Dizzy" |
    | "Acute Yellowitis" | 0.6068906083054821 | "Itchy" |
    +---------------------------------------------------------+
    72 rows
    ready to start consuming query after 339 ms, results consumed after another 28 ms
    Created 59 relationships, Set 59 properties
  5. Ensure that the as yet non-existent types can be used:

    CALL db.createRelationshipType('DIAGNOSIS');
    CALL db.createProperty('by');
    CALL db.createProperty('date');
    CALL db.createProperty('description');
    CALL db.createProperty('created_at');
    CALL db.createProperty('updated_at');
    Result
    0 rows
    ready to start consuming query after 22 ms, results consumed after another 0 ms
    0 rows
    ready to start consuming query after 17 ms, results consumed after another 0 ms
    0 rows
    ready to start consuming query after 7 ms, results consumed after another 0 ms
    0 rows
    ready to start consuming query after 7 ms, results consumed after another 0 ms
    0 rows
    ready to start consuming query after 8 ms, results consumed after another 0 ms
    0 rows
    ready to start consuming query after 7 ms, results consumed after another 0 ms
  6. Create some data for patients:

    WITH
     ['Jack','Mary','Sally','Mark','Joe','Jane','Bob','Ally'] AS firstnames,
     ['Anderson','Jackson','Svensson','Smith','Stone'] AS surnames,
     ['mymail.com','example.com','other.org','net.net'] AS domains
    UNWIND range(0,100) AS uid
    WITH 1234567+uid AS ssn,
     firstnames[uid%size(firstnames)] AS firstname,
     surnames[uid%size(surnames)] AS surname,
     domains[uid%size(domains)] AS domain
    WITH ssn, firstname, surname,
     tolower(firstname + '.' + surname + '@' + domain) AS email,
     toInteger(1500000000000 * rand()) AS ts
    MERGE (p:Patient {ssn:ssn})
    ON CREATE SET p.name = firstname + ' ' + surname,
     p.email = email,
     p.address = '1 secret way, downtown',
     p.dateOfBirth = date(datetime({epochmillis:ts}))
    RETURN count(p);
    Result
    +----------+
    | count(p) |
    +----------+
    | 101 |
    +----------+
    1 row
    ready to start consuming query after 49 ms, results consumed after another 38 ms
    Added 101 nodes, Set 505 properties, Added 101 labels
  7. Create relationships between patients and symptoms:

    MATCH (s:Symptom) WITH collect(s) as symptoms
    WITH symptoms, size(symptoms) / 2 as maxsym, 1500000000000 AS base, 75477004177 AS diff
    MATCH (p:Patient)
    UNWIND range(0,maxsym) as symi
    WITH p, symi, symptoms, toInteger(size(symptoms) * rand()) as si, rand()/2 + 0.5 AS prob, base + toInteger(diff * rand()) AS ts
    WITH p, symptoms[si] AS s, prob, ts
    MERGE (p)-[h:HAS]->(s)
    ON CREATE SET h.date = date(datetime({epochmillis:ts}))
    RETURN p.name, p.dateOfBirth, h.date, s.name;
    Result
    +------------------------------------------------------------+
    | p.name | p.dateOfBirth | h.date | s.name |
    +------------------------------------------------------------+
    | "Jack Anderson" | 1981年01月10日 | 2019年03月03日 | "Angry" |
    | "Jack Anderson" | 1981年01月10日 | 2018年05月05日 | "Sad" |
    | "Jack Anderson" | 1981年01月10日 | 2018年06月05日 | "Sore" |
    | "Jack Anderson" | 1981年01月10日 | 2017年11月17日 | "Itchy" |
    | "Jack Anderson" | 1981年01月10日 | 2017年10月02日 | "Dizzy" |
    | "Jack Anderson" | 1981年01月10日 | 2019年11月01日 | "Red" |
    | "Mary Jackson" | 1983年05月24日 | 2018年03月30日 | "Scratchy" |
    | "Mary Jackson" | 1983年05月24日 | 2018年03月08日 | "Pale" |
    | "Mary Jackson" | 1983年05月24日 | 2019年05月03日 | "Dizzy" |
    | "Mary Jackson" | 1983年05月24日 | 2019年08月16日 | "Red" |
    | "Mary Jackson" | 1983年05月24日 | 2018年07月07日 | "Inflamed" |
    | "Mary Jackson" | 1983年05月24日 | 2018年07月07日 | "Inflamed" |
    | "Sally Svensson" | 2011年04月03日 | 2018年02月12日 | "Sore" |
    | "Sally Svensson" | 2011年04月03日 | 2019年07月23日 | "Pale" |
    | "Sally Svensson" | 2011年04月03日 | 2019年04月17日 | "Sad" |
    | "Sally Svensson" | 2011年04月03日 | 2017年09月25日 | "Red" |
    | "Sally Svensson" | 2011年04月03日 | 2017年08月14日 | "Swollen" |
    | "Sally Svensson" | 2011年04月03日 | 2017年08月14日 | "Swollen" |
    | "Mark Smith" | 1998年08月26日 | 2018年08月25日 | "Inflamed" |
    | "Mark Smith" | 1998年08月26日 | 2018年08月25日 | "Inflamed" |
    | "Mark Smith" | 1998年08月26日 | 2018年05月14日 | "Itchy" |
    | "Mark Smith" | 1998年08月26日 | 2019年07月02日 | "Dizzy" |
    | "Mark Smith" | 1998年08月26日 | 2018年02月27日 | "Sad" |
    | "Mark Smith" | 1998年08月26日 | 2018年10月07日 | "Swollen" |
    | "Joe Stone" | 1972年10月20日 | 2019年05月16日 | "Red" |
    | "Joe Stone" | 1972年10月20日 | 2018年08月31日 | "Inflamed" |
    | "Joe Stone" | 1972年10月20日 | 2018年08月31日 | "Inflamed" |
    | "Joe Stone" | 1972年10月20日 | 2017年07月21日 | "Sore" |
    | "Joe Stone" | 1972年10月20日 | 2018年08月31日 | "Inflamed" |
    | "Joe Stone" | 1972年10月20日 | 2017年10月21日 | "Itchy" |
    | "Jane Anderson" | 2001年10月18日 | 2018年07月04日 | "Scratchy" |
    | "Jane Anderson" | 2001年10月18日 | 2019年02月09日 | "Dizzy" |
    | "Jane Anderson" | 2001年10月18日 | 2018年05月03日 | "Pale" |
    | "Jane Anderson" | 2001年10月18日 | 2019年08月13日 | "Angry" |
    | "Jane Anderson" | 2001年10月18日 | 2018年05月03日 | "Pale" |
    | "Jane Anderson" | 2001年10月18日 | 2019年11月12日 | "Swollen" |
    | "Bob Jackson" | 1997年08月20日 | 2019年05月03日 | "Sad" |
    | "Bob Jackson" | 1997年08月20日 | 2019年03月14日 | "Red" |
    | "Bob Jackson" | 1997年08月20日 | 2019年03月01日 | "Angry" |
    | "Bob Jackson" | 1997年08月20日 | 2018年03月10日 | "Sore" |
    | "Bob Jackson" | 1997年08月20日 | 2018年03月10日 | "Sore" |
    | "Bob Jackson" | 1997年08月20日 | 2019年05月03日 | "Sad" |
    | "Ally Svensson" | 2008年05月25日 | 2019年06月06日 | "Sore" |
    | "Ally Svensson" | 2008年05月25日 | 2019年11月04日 | "Sad" |
    | "Ally Svensson" | 2008年05月25日 | 2018年10月04日 | "Scratchy" |
    | "Ally Svensson" | 2008年05月25日 | 2017年11月28日 | "Inflamed" |
    | "Ally Svensson" | 2008年05月25日 | 2018年10月29日 | "Itchy" |
    | "Ally Svensson" | 2008年05月25日 | 2019年08月08日 | "Angry" |
    | "Jack Smith" | 1974年07月02日 | 2018年01月15日 | "Itchy" |
    | "Jack Smith" | 1974年07月02日 | 2019年02月12日 | "Angry" |
    | "Jack Smith" | 1974年07月02日 | 2017年10月16日 | "Dizzy" |
    | "Jack Smith" | 1974年07月02日 | 2018年01月03日 | "Red" |
    | "Jack Smith" | 1974年07月02日 | 2018年01月15日 | "Itchy" |
    | "Jack Smith" | 1974年07月02日 | 2017年11月14日 | "Pale" |
    | "Mary Stone" | 1983年09月27日 | 2018年01月14日 | "Dizzy" |
    | "Mary Stone" | 1983年09月27日 | 2019年03月24日 | "Swollen" |
    | "Mary Stone" | 1983年09月27日 | 2018年04月07日 | "Angry" |
    | "Mary Stone" | 1983年09月27日 | 2019年03月24日 | "Swollen" |
    | "Mary Stone" | 1983年09月27日 | 2018年01月14日 | "Dizzy" |
    | "Mary Stone" | 1983年09月27日 | 2017年11月27日 | "Sore" |
    | "Sally Anderson" | 2009年12月14日 | 2019年08月21日 | "Swollen" |
    | "Sally Anderson" | 2009年12月14日 | 2018年02月23日 | "Sore" |
    | "Sally Anderson" | 2009年12月14日 | 2018年06月05日 | "Scratchy" |
    | "Sally Anderson" | 2009年12月14日 | 2018年02月23日 | "Sore" |
    | "Sally Anderson" | 2009年12月14日 | 2017年08月20日 | "Pale" |
    | "Sally Anderson" | 2009年12月14日 | 2019年05月25日 | "Itchy" |
    | "Mark Jackson" | 1970年11月29日 | 2018年09月27日 | "Sore" |
    | "Mark Jackson" | 1970年11月29日 | 2017年12月27日 | "Angry" |
    | "Mark Jackson" | 1970年11月29日 | 2017年12月26日 | "Swollen" |
    | "Mark Jackson" | 1970年11月29日 | 2018年09月27日 | "Sore" |
    | "Mark Jackson" | 1970年11月29日 | 2018年02月01日 | "Inflamed" |
    | "Mark Jackson" | 1970年11月29日 | 2018年12月19日 | "Pale" |
    | "Joe Svensson" | 1972年02月12日 | 2017年07月27日 | "Sad" |
    | "Joe Svensson" | 1972年02月12日 | 2019年07月27日 | "Itchy" |
    | "Joe Svensson" | 1972年02月12日 | 2019年06月20日 | "Sore" |
    | "Joe Svensson" | 1972年02月12日 | 2019年11月09日 | "Inflamed" |
    | "Joe Svensson" | 1972年02月12日 | 2019年07月27日 | "Itchy" |
    | "Joe Svensson" | 1972年02月12日 | 2019年07月27日 | "Itchy" |
    | "Jane Smith" | 2013年04月09日 | 2018年12月13日 | "Swollen" |
    | "Jane Smith" | 2013年04月09日 | 2018年12月13日 | "Swollen" |
    | "Jane Smith" | 2013年04月09日 | 2019年03月13日 | "Red" |
    | "Jane Smith" | 2013年04月09日 | 2019年01月25日 | "Dizzy" |
    | "Jane Smith" | 2013年04月09日 | 2017年07月25日 | "Angry" |
    | "Jane Smith" | 2013年04月09日 | 2018年04月11日 | "Inflamed" |
    | "Bob Stone" | 2012年04月11日 | 2018年09月04日 | "Inflamed" |
    | "Bob Stone" | 2012年04月11日 | 2017年08月07日 | "Red" |
    | "Bob Stone" | 2012年04月11日 | 2019年07月11日 | "Swollen" |
    | "Bob Stone" | 2012年04月11日 | 2019年07月11日 | "Swollen" |
    | "Bob Stone" | 2012年04月11日 | 2019年07月14日 | "Sore" |
    | "Bob Stone" | 2012年04月11日 | 2017年11月18日 | "Itchy" |
    | "Ally Anderson" | 2000年05月24日 | 2018年11月27日 | "Itchy" |
    | "Ally Anderson" | 2000年05月24日 | 2018年02月10日 | "Pale" |
    | "Ally Anderson" | 2000年05月24日 | 2017年07月25日 | "Red" |
    | "Ally Anderson" | 2000年05月24日 | 2018年03月19日 | "Sad" |
    | "Ally Anderson" | 2000年05月24日 | 2017年07月25日 | "Red" |
    | "Ally Anderson" | 2000年05月24日 | 2017年07月25日 | "Red" |
    | "Jack Jackson" | 1988年03月13日 | 2018年06月29日 | "Sad" |
    | "Jack Jackson" | 1988年03月13日 | 2019年06月25日 | "Sore" |
    | "Jack Jackson" | 1988年03月13日 | 2019年05月27日 | "Inflamed" |
    | "Jack Jackson" | 1988年03月13日 | 2018年07月09日 | "Angry" |
    | "Jack Jackson" | 1988年03月13日 | 2018年04月23日 | "Pale" |
    | "Jack Jackson" | 1988年03月13日 | 2019年05月15日 | "Dizzy" |
    | "Mary Svensson" | 2008年01月19日 | 2018年03月16日 | "Dizzy" |
    | "Mary Svensson" | 2008年01月19日 | 2018年04月14日 | "Red" |
    | "Mary Svensson" | 2008年01月19日 | 2018年10月25日 | "Pale" |
    | "Mary Svensson" | 2008年01月19日 | 2019年07月15日 | "Swollen" |
    | "Mary Svensson" | 2008年01月19日 | 2019年07月15日 | "Swollen" |
    | "Mary Svensson" | 2008年01月19日 | 2018年04月14日 | "Red" |
    | "Sally Smith" | 1977年03月20日 | 2019年02月23日 | "Dizzy" |
    | "Sally Smith" | 1977年03月20日 | 2017年11月28日 | "Red" |
    | "Sally Smith" | 1977年03月20日 | 2018年06月23日 | "Scratchy" |
    | "Sally Smith" | 1977年03月20日 | 2017年10月28日 | "Sad" |
    | "Sally Smith" | 1977年03月20日 | 2017年11月28日 | "Red" |
    | "Sally Smith" | 1977年03月20日 | 2018年10月05日 | "Inflamed" |
    | "Mark Stone" | 1986年06月15日 | 2019年11月24日 | "Pale" |
    | "Mark Stone" | 1986年06月15日 | 2018年07月30日 | "Itchy" |
    | "Mark Stone" | 1986年06月15日 | 2018年07月30日 | "Itchy" |
    | "Mark Stone" | 1986年06月15日 | 2017年09月10日 | "Dizzy" |
    | "Mark Stone" | 1986年06月15日 | 2018年07月18日 | "Red" |
    | "Mark Stone" | 1986年06月15日 | 2019年08月15日 | "Sore" |
    | "Joe Anderson" | 1980年09月06日 | 2019年06月19日 | "Dizzy" |
    | "Joe Anderson" | 1980年09月06日 | 2017年11月28日 | "Red" |
    | "Joe Anderson" | 1980年09月06日 | 2019年08月12日 | "Scratchy" |
    | "Joe Anderson" | 1980年09月06日 | 2019年08月12日 | "Scratchy" |
    | "Joe Anderson" | 1980年09月06日 | 2019年06月19日 | "Dizzy" |
    | "Joe Anderson" | 1980年09月06日 | 2017年07月16日 | "Inflamed" |
    | "Jane Jackson" | 2016年02月20日 | 2018年04月03日 | "Swollen" |
    | "Jane Jackson" | 2016年02月20日 | 2018年02月21日 | "Pale" |
    | "Jane Jackson" | 2016年02月20日 | 2018年07月17日 | "Angry" |
    | "Jane Jackson" | 2016年02月20日 | 2018年01月22日 | "Sore" |
    | "Jane Jackson" | 2016年02月20日 | 2018年01月22日 | "Sore" |
    | "Jane Jackson" | 2016年02月20日 | 2017年09月28日 | "Dizzy" |
    | "Bob Svensson" | 1983年08月04日 | 2019年02月02日 | "Pale" |
    | "Bob Svensson" | 1983年08月04日 | 2018年12月01日 | "Dizzy" |
    | "Bob Svensson" | 1983年08月04日 | 2019年08月07日 | "Sad" |
    | "Bob Svensson" | 1983年08月04日 | 2018年11月18日 | "Swollen" |
    | "Bob Svensson" | 1983年08月04日 | 2018年12月25日 | "Scratchy" |
    | "Bob Svensson" | 1983年08月04日 | 2018年04月09日 | "Inflamed" |
    | "Ally Smith" | 2012年03月01日 | 2018年03月28日 | "Inflamed" |
    | "Ally Smith" | 2012年03月01日 | 2018年03月28日 | "Inflamed" |
    | "Ally Smith" | 2012年03月01日 | 2018年06月09日 | "Scratchy" |
    | "Ally Smith" | 2012年03月01日 | 2019年01月25日 | "Angry" |
    | "Ally Smith" | 2012年03月01日 | 2018年09月06日 | "Pale" |
    | "Ally Smith" | 2012年03月01日 | 2018年12月04日 | "Dizzy" |
    | "Jack Stone" | 2009年11月08日 | 2019年01月18日 | "Pale" |
    | "Jack Stone" | 2009年11月08日 | 2018年03月29日 | "Angry" |
    | "Jack Stone" | 2009年11月08日 | 2019年10月22日 | "Inflamed" |
    | "Jack Stone" | 2009年11月08日 | 2019年01月18日 | "Pale" |
    | "Jack Stone" | 2009年11月08日 | 2017年12月09日 | "Itchy" |
    | "Jack Stone" | 2009年11月08日 | 2018年10月27日 | "Red" |
    | "Mary Anderson" | 1991年11月25日 | 2018年01月02日 | "Angry" |
    | "Mary Anderson" | 1991年11月25日 | 2018年01月02日 | "Angry" |
    | "Mary Anderson" | 1991年11月25日 | 2017年11月01日 | "Inflamed" |
    | "Mary Anderson" | 1991年11月25日 | 2017年12月16日 | "Sore" |
    | "Mary Anderson" | 1991年11月25日 | 2018年01月02日 | "Angry" |
    | "Mary Anderson" | 1991年11月25日 | 2018年03月22日 | "Red" |
    | "Sally Jackson" | 2008年11月09日 | 2019年07月02日 | "Inflamed" |
    | "Sally Jackson" | 2008年11月09日 | 2018年02月24日 | "Red" |
    | "Sally Jackson" | 2008年11月09日 | 2019年08月07日 | "Swollen" |
    | "Sally Jackson" | 2008年11月09日 | 2019年04月05日 | "Sore" |
    | "Sally Jackson" | 2008年11月09日 | 2019年07月02日 | "Inflamed" |
    | "Sally Jackson" | 2008年11月09日 | 2019年02月23日 | "Scratchy" |
    | "Mark Svensson" | 1979年06月22日 | 2019年08月09日 | "Itchy" |
    | "Mark Svensson" | 1979年06月22日 | 2019年05月11日 | "Swollen" |
    | "Mark Svensson" | 1979年06月22日 | 2018年08月11日 | "Inflamed" |
    | "Mark Svensson" | 1979年06月22日 | 2019年08月09日 | "Itchy" |
    | "Mark Svensson" | 1979年06月22日 | 2017年10月11日 | "Sad" |
    | "Mark Svensson" | 1979年06月22日 | 2019年09月22日 | "Scratchy" |
    | "Joe Smith" | 2008年07月03日 | 2017年08月24日 | "Sore" |
    | "Joe Smith" | 2008年07月03日 | 2018年12月03日 | "Red" |
    | "Joe Smith" | 2008年07月03日 | 2018年12月03日 | "Red" |
    | "Joe Smith" | 2008年07月03日 | 2018年08月20日 | "Inflamed" |
    | "Joe Smith" | 2008年07月03日 | 2018年12月03日 | "Red" |
    | "Joe Smith" | 2008年07月03日 | 2019年04月17日 | "Angry" |
    | "Jane Stone" | 1977年11月25日 | 2018年03月19日 | "Scratchy" |
    | "Jane Stone" | 1977年11月25日 | 2017年08月18日 | "Dizzy" |
    | "Jane Stone" | 1977年11月25日 | 2017年12月09日 | "Red" |
    | "Jane Stone" | 1977年11月25日 | 2018年06月14日 | "Swollen" |
    | "Jane Stone" | 1977年11月25日 | 2018年08月22日 | "Pale" |
    | "Jane Stone" | 1977年11月25日 | 2018年08月22日 | "Pale" |
    | "Bob Anderson" | 1970年04月27日 | 2019年10月17日 | "Scratchy" |
    | "Bob Anderson" | 1970年04月27日 | 2018年06月16日 | "Red" |
    | "Bob Anderson" | 1970年04月27日 | 2017年11月07日 | "Itchy" |
    | "Bob Anderson" | 1970年04月27日 | 2018年12月11日 | "Pale" |
    | "Bob Anderson" | 1970年04月27日 | 2017年11月07日 | "Itchy" |
    | "Bob Anderson" | 1970年04月27日 | 2019年02月26日 | "Swollen" |
    | "Ally Jackson" | 1982年01月12日 | 2019年06月15日 | "Sad" |
    | "Ally Jackson" | 1982年01月12日 | 2018年01月12日 | "Sore" |
    | "Ally Jackson" | 1982年01月12日 | 2019年06月15日 | "Sad" |
    | "Ally Jackson" | 1982年01月12日 | 2018年01月12日 | "Sore" |
    | "Ally Jackson" | 1982年01月12日 | 2018年04月19日 | "Itchy" |
    | "Ally Jackson" | 1982年01月12日 | 2019年04月06日 | "Red" |
    | "Jack Svensson" | 2012年08月22日 | 2017年12月10日 | "Scratchy" |
    | "Jack Svensson" | 2012年08月22日 | 2018年08月25日 | "Pale" |
    | "Jack Svensson" | 2012年08月22日 | 2017年12月10日 | "Scratchy" |
    | "Jack Svensson" | 2012年08月22日 | 2018年12月07日 | "Swollen" |
    | "Jack Svensson" | 2012年08月22日 | 2018年08月25日 | "Pale" |
    | "Jack Svensson" | 2012年08月22日 | 2018年04月30日 | "Red" |
    | "Mary Smith" | 2002年11月27日 | 2018年07月26日 | "Red" |
    | "Mary Smith" | 2002年11月27日 | 2018年04月09日 | "Dizzy" |
    | "Mary Smith" | 2002年11月27日 | 2018年08月08日 | "Pale" |
    | "Mary Smith" | 2002年11月27日 | 2018年08月28日 | "Sore" |
    | "Mary Smith" | 2002年11月27日 | 2018年07月26日 | "Red" |
    | "Mary Smith" | 2002年11月27日 | 2019年09月16日 | "Itchy" |
    | "Sally Stone" | 2001年04月25日 | 2018年02月13日 | "Sore" |
    | "Sally Stone" | 2001年04月25日 | 2019年05月03日 | "Itchy" |
    | "Sally Stone" | 2001年04月25日 | 2019年09月25日 | "Dizzy" |
    | "Sally Stone" | 2001年04月25日 | 2018年05月10日 | "Inflamed" |
    | "Sally Stone" | 2001年04月25日 | 2019年09月03日 | "Scratchy" |
    | "Sally Stone" | 2001年04月25日 | 2018年05月10日 | "Inflamed" |
    | "Mark Anderson" | 2007年06月19日 | 2019年04月22日 | "Angry" |
    | "Mark Anderson" | 2007年06月19日 | 2018年09月23日 | "Scratchy" |
    | "Mark Anderson" | 2007年06月19日 | 2019年03月10日 | "Pale" |
    | "Mark Anderson" | 2007年06月19日 | 2019年03月10日 | "Pale" |
    | "Mark Anderson" | 2007年06月19日 | 2018年09月23日 | "Scratchy" |
    | "Mark Anderson" | 2007年06月19日 | 2017年11月16日 | "Sad" |
    | "Joe Jackson" | 1991年10月12日 | 2018年06月27日 | "Red" |
    | "Joe Jackson" | 1991年10月12日 | 2018年10月26日 | "Pale" |
    | "Joe Jackson" | 1991年10月12日 | 2018年10月30日 | "Sore" |
    | "Joe Jackson" | 1991年10月12日 | 2018年10月30日 | "Sore" |
    | "Joe Jackson" | 1991年10月12日 | 2018年10月26日 | "Pale" |
    | "Joe Jackson" | 1991年10月12日 | 2019年01月06日 | "Swollen" |
    | "Jane Svensson" | 1982年07月02日 | 2019年11月29日 | "Red" |
    | "Jane Svensson" | 1982年07月02日 | 2017年12月07日 | "Angry" |
    | "Jane Svensson" | 1982年07月02日 | 2019年04月05日 | "Swollen" |
    | "Jane Svensson" | 1982年07月02日 | 2019年04月05日 | "Swollen" |
    | "Jane Svensson" | 1982年07月02日 | 2018年12月10日 | "Sad" |
    | "Jane Svensson" | 1982年07月02日 | 2019年11月09日 | "Inflamed" |
    | "Bob Smith" | 1981年10月29日 | 2018年07月21日 | "Sad" |
    | "Bob Smith" | 1981年10月29日 | 2019年09月15日 | "Itchy" |
    | "Bob Smith" | 1981年10月29日 | 2019年04月18日 | "Scratchy" |
    | "Bob Smith" | 1981年10月29日 | 2019年05月12日 | "Swollen" |
    | "Bob Smith" | 1981年10月29日 | 2018年07月21日 | "Sad" |
    | "Bob Smith" | 1981年10月29日 | 2019年02月04日 | "Pale" |
    | "Ally Stone" | 1980年12月13日 | 2018年08月02日 | "Red" |
    | "Ally Stone" | 1980年12月13日 | 2017年09月04日 | "Dizzy" |
    | "Ally Stone" | 1980年12月13日 | 2017年09月04日 | "Dizzy" |
    | "Ally Stone" | 1980年12月13日 | 2017年09月13日 | "Pale" |
    | "Ally Stone" | 1980年12月13日 | 2018年01月21日 | "Sad" |
    | "Ally Stone" | 1980年12月13日 | 2017年09月04日 | "Dizzy" |
    | "Jack Anderson" | 1998年11月09日 | 2019年01月22日 | "Swollen" |
    | "Jack Anderson" | 1998年11月09日 | 2019年07月14日 | "Red" |
    | "Jack Anderson" | 1998年11月09日 | 2019年05月21日 | "Inflamed" |
    | "Jack Anderson" | 1998年11月09日 | 2019年05月21日 | "Inflamed" |
    | "Jack Anderson" | 1998年11月09日 | 2019年06月18日 | "Itchy" |
    | "Jack Anderson" | 1998年11月09日 | 2019年01月22日 | "Swollen" |
    | "Mary Jackson" | 1974年09月25日 | 2018年12月10日 | "Itchy" |
    | "Mary Jackson" | 1974年09月25日 | 2017年10月13日 | "Swollen" |
    | "Mary Jackson" | 1974年09月25日 | 2018年02月26日 | "Red" |
    | "Mary Jackson" | 1974年09月25日 | 2018年01月25日 | "Sad" |
    | "Mary Jackson" | 1974年09月25日 | 2017年08月05日 | "Inflamed" |
    | "Mary Jackson" | 1974年09月25日 | 2018年09月22日 | "Scratchy" |
    | "Sally Svensson" | 1987年06月05日 | 2018年06月23日 | "Red" |
    | "Sally Svensson" | 1987年06月05日 | 2017年12月31日 | "Sad" |
    | "Sally Svensson" | 1987年06月05日 | 2017年12月25日 | "Sore" |
    | "Sally Svensson" | 1987年06月05日 | 2018年08月10日 | "Dizzy" |
    | "Sally Svensson" | 1987年06月05日 | 2017年12月31日 | "Sad" |
    | "Sally Svensson" | 1987年06月05日 | 2019年10月31日 | "Angry" |
    | "Mark Smith" | 1991年08月30日 | 2019年07月28日 | "Swollen" |
    | "Mark Smith" | 1991年08月30日 | 2019年01月14日 | "Itchy" |
    | "Mark Smith" | 1991年08月30日 | 2018年11月09日 | "Sad" |
    | "Mark Smith" | 1991年08月30日 | 2019年07月28日 | "Swollen" |
    | "Mark Smith" | 1991年08月30日 | 2019年06月09日 | "Red" |
    | "Mark Smith" | 1991年08月30日 | 2017年10月09日 | "Scratchy" |
    | "Joe Stone" | 1999年08月23日 | 2017年09月15日 | "Itchy" |
    | "Joe Stone" | 1999年08月23日 | 2019年08月12日 | "Dizzy" |
    | "Joe Stone" | 1999年08月23日 | 2018年12月06日 | "Sore" |
    | "Joe Stone" | 1999年08月23日 | 2018年06月04日 | "Swollen" |
    | "Joe Stone" | 1999年08月23日 | 2019年11月14日 | "Inflamed" |
    | "Joe Stone" | 1999年08月23日 | 2019年05月19日 | "Scratchy" |
    | "Jane Anderson" | 1988年07月16日 | 2019年08月15日 | "Red" |
    | "Jane Anderson" | 1988年07月16日 | 2018年09月26日 | "Sore" |
    | "Jane Anderson" | 1988年07月16日 | 2018年10月22日 | "Pale" |
    | "Jane Anderson" | 1988年07月16日 | 2018年03月20日 | "Inflamed" |
    | "Jane Anderson" | 1988年07月16日 | 2019年05月13日 | "Dizzy" |
    | "Jane Anderson" | 1988年07月16日 | 2019年05月13日 | "Dizzy" |
    | "Bob Jackson" | 1974年09月23日 | 2019年01月07日 | "Sore" |
    | "Bob Jackson" | 1974年09月23日 | 2017年10月13日 | "Scratchy" |
    | "Bob Jackson" | 1974年09月23日 | 2019年07月20日 | "Swollen" |
    | "Bob Jackson" | 1974年09月23日 | 2017年11月23日 | "Red" |
    | "Bob Jackson" | 1974年09月23日 | 2019年04月07日 | "Sad" |
    | "Bob Jackson" | 1974年09月23日 | 2019年08月23日 | "Itchy" |
    | "Ally Svensson" | 2006年11月13日 | 2018年07月22日 | "Pale" |
    | "Ally Svensson" | 2006年11月13日 | 2018年10月13日 | "Itchy" |
    | "Ally Svensson" | 2006年11月13日 | 2017年10月07日 | "Sad" |
    | "Ally Svensson" | 2006年11月13日 | 2018年10月13日 | "Itchy" |
    | "Ally Svensson" | 2006年11月13日 | 2018年06月20日 | "Dizzy" |
    | "Ally Svensson" | 2006年11月13日 | 2019年10月08日 | "Scratchy" |
    | "Jack Smith" | 2017年05月17日 | 2018年03月20日 | "Red" |
    | "Jack Smith" | 2017年05月17日 | 2019年01月13日 | "Swollen" |
    | "Jack Smith" | 2017年05月17日 | 2018年08月06日 | "Itchy" |
    | "Jack Smith" | 2017年05月17日 | 2018年07月18日 | "Scratchy" |
    | "Jack Smith" | 2017年05月17日 | 2018年06月10日 | "Sore" |
    | "Jack Smith" | 2017年05月17日 | 2018年03月20日 | "Red" |
    | "Mary Stone" | 2011年06月20日 | 2019年02月07日 | "Pale" |
    | "Mary Stone" | 2011年06月20日 | 2018年12月07日 | "Itchy" |
    | "Mary Stone" | 2011年06月20日 | 2019年09月17日 | "Scratchy" |
    | "Mary Stone" | 2011年06月20日 | 2017年08月02日 | "Sore" |
    | "Mary Stone" | 2011年06月20日 | 2019年09月17日 | "Scratchy" |
    | "Mary Stone" | 2011年06月20日 | 2019年02月07日 | "Pale" |
    | "Sally Anderson" | 1970年12月02日 | 2018年10月20日 | "Swollen" |
    | "Sally Anderson" | 1970年12月02日 | 2019年02月05日 | "Scratchy" |
    | "Sally Anderson" | 1970年12月02日 | 2019年11月12日 | "Pale" |
    | "Sally Anderson" | 1970年12月02日 | 2018年03月21日 | "Angry" |
    | "Sally Anderson" | 1970年12月02日 | 2019年07月21日 | "Inflamed" |
    | "Sally Anderson" | 1970年12月02日 | 2019年07月21日 | "Inflamed" |
    | "Mark Jackson" | 2003年07月09日 | 2018年12月20日 | "Sore" |
    | "Mark Jackson" | 2003年07月09日 | 2018年04月13日 | "Itchy" |
    | "Mark Jackson" | 2003年07月09日 | 2018年11月08日 | "Inflamed" |
    | "Mark Jackson" | 2003年07月09日 | 2019年09月17日 | "Swollen" |
    | "Mark Jackson" | 2003年07月09日 | 2018年04月11日 | "Dizzy" |
    | "Mark Jackson" | 2003年07月09日 | 2018年12月20日 | "Sore" |
    | "Joe Svensson" | 2000年03月07日 | 2019年01月31日 | "Angry" |
    | "Joe Svensson" | 2000年03月07日 | 2018年03月29日 | "Sore" |
    | "Joe Svensson" | 2000年03月07日 | 2019年10月26日 | "Pale" |
    | "Joe Svensson" | 2000年03月07日 | 2019年01月31日 | "Angry" |
    | "Joe Svensson" | 2000年03月07日 | 2018年01月01日 | "Scratchy" |
    | "Joe Svensson" | 2000年03月07日 | 2018年03月29日 | "Sore" |
    | "Jane Smith" | 2012年05月14日 | 2019年03月18日 | "Pale" |
    | "Jane Smith" | 2012年05月14日 | 2018年08月15日 | "Swollen" |
    | "Jane Smith" | 2012年05月14日 | 2018年01月16日 | "Sore" |
    | "Jane Smith" | 2012年05月14日 | 2018年03月14日 | "Scratchy" |
    | "Jane Smith" | 2012年05月14日 | 2018年05月23日 | "Inflamed" |
    | "Jane Smith" | 2012年05月14日 | 2019年07月06日 | "Red" |
    | "Bob Stone" | 2011年06月07日 | 2018年03月12日 | "Itchy" |
    | "Bob Stone" | 2011年06月07日 | 2018年05月20日 | "Sad" |
    | "Bob Stone" | 2011年06月07日 | 2017年08月12日 | "Red" |
    | "Bob Stone" | 2011年06月07日 | 2018年01月13日 | "Swollen" |
    | "Bob Stone" | 2011年06月07日 | 2019年01月13日 | "Angry" |
    | "Bob Stone" | 2011年06月07日 | 2018年03月12日 | "Itchy" |
    | "Ally Anderson" | 1972年05月20日 | 2018年09月27日 | "Pale" |
    | "Ally Anderson" | 1972年05月20日 | 2017年08月11日 | "Inflamed" |
    | "Ally Anderson" | 1972年05月20日 | 2017年08月23日 | "Sad" |
    | "Ally Anderson" | 1972年05月20日 | 2019年06月09日 | "Dizzy" |
    | "Ally Anderson" | 1972年05月20日 | 2018年10月08日 | "Scratchy" |
    | "Ally Anderson" | 1972年05月20日 | 2018年06月13日 | "Swollen" |
    | "Jack Jackson" | 1985年09月11日 | 2019年01月06日 | "Red" |
    | "Jack Jackson" | 1985年09月11日 | 2018年02月05日 | "Sore" |
    | "Jack Jackson" | 1985年09月11日 | 2018年09月10日 | "Scratchy" |
    | "Jack Jackson" | 1985年09月11日 | 2019年10月17日 | "Dizzy" |
    | "Jack Jackson" | 1985年09月11日 | 2018年07月07日 | "Angry" |
    | "Jack Jackson" | 1985年09月11日 | 2018年02月05日 | "Sore" |
    | "Mary Svensson" | 1987年09月18日 | 2018年08月06日 | "Red" |
    | "Mary Svensson" | 1987年09月18日 | 2018年03月03日 | "Scratchy" |
    | "Mary Svensson" | 1987年09月18日 | 2018年10月13日 | "Sad" |
    | "Mary Svensson" | 1987年09月18日 | 2019年02月03日 | "Sore" |
    | "Mary Svensson" | 1987年09月18日 | 2018年08月06日 | "Red" |
    | "Mary Svensson" | 1987年09月18日 | 2018年08月06日 | "Red" |
    | "Sally Smith" | 2005年07月05日 | 2018年05月26日 | "Dizzy" |
    | "Sally Smith" | 2005年07月05日 | 2018年11月02日 | "Sad" |
    | "Sally Smith" | 2005年07月05日 | 2018年05月26日 | "Dizzy" |
    | "Sally Smith" | 2005年07月05日 | 2017年10月26日 | "Inflamed" |
    | "Sally Smith" | 2005年07月05日 | 2018年07月24日 | "Angry" |
    | "Sally Smith" | 2005年07月05日 | 2018年05月26日 | "Dizzy" |
    | "Mark Stone" | 2011年01月01日 | 2019年01月24日 | "Red" |
    | "Mark Stone" | 2011年01月01日 | 2018年02月26日 | "Scratchy" |
    | "Mark Stone" | 2011年01月01日 | 2018年11月11日 | "Swollen" |
    | "Mark Stone" | 2011年01月01日 | 2017年12月16日 | "Sore" |
    | "Mark Stone" | 2011年01月01日 | 2018年02月26日 | "Scratchy" |
    | "Mark Stone" | 2011年01月01日 | 2019年09月13日 | "Sad" |
    | "Joe Anderson" | 1981年12月16日 | 2017年11月29日 | "Pale" |
    | "Joe Anderson" | 1981年12月16日 | 2018年12月13日 | "Dizzy" |
    | "Joe Anderson" | 1981年12月16日 | 2018年06月05日 | "Swollen" |
    | "Joe Anderson" | 1981年12月16日 | 2018年09月27日 | "Sad" |
    | "Joe Anderson" | 1981年12月16日 | 2017年09月12日 | "Inflamed" |
    | "Joe Anderson" | 1981年12月16日 | 2019年10月10日 | "Sore" |
    | "Jane Jackson" | 1989年10月16日 | 2019年04月22日 | "Dizzy" |
    | "Jane Jackson" | 1989年10月16日 | 2019年04月30日 | "Swollen" |
    | "Jane Jackson" | 1989年10月16日 | 2018年04月19日 | "Red" |
    | "Jane Jackson" | 1989年10月16日 | 2018年09月28日 | "Inflamed" |
    | "Jane Jackson" | 1989年10月16日 | 2019年07月19日 | "Scratchy" |
    | "Jane Jackson" | 1989年10月16日 | 2018年05月19日 | "Sad" |
    | "Bob Svensson" | 2003年05月06日 | 2019年11月05日 | "Sore" |
    | "Bob Svensson" | 2003年05月06日 | 2018年08月09日 | "Scratchy" |
    | "Bob Svensson" | 2003年05月06日 | 2018年11月22日 | "Inflamed" |
    | "Bob Svensson" | 2003年05月06日 | 2018年02月14日 | "Angry" |
    | "Bob Svensson" | 2003年05月06日 | 2018年11月22日 | "Inflamed" |
    | "Bob Svensson" | 2003年05月06日 | 2018年02月25日 | "Itchy" |
    | "Ally Smith" | 1979年08月06日 | 2019年10月25日 | "Pale" |
    | "Ally Smith" | 1979年08月06日 | 2019年11月25日 | "Sore" |
    | "Ally Smith" | 1979年08月06日 | 2019年10月19日 | "Dizzy" |
    | "Ally Smith" | 1979年08月06日 | 2018年01月06日 | "Sad" |
    | "Ally Smith" | 1979年08月06日 | 2019年03月12日 | "Red" |
    | "Ally Smith" | 1979年08月06日 | 2019年05月25日 | "Itchy" |
    | "Jack Stone" | 2003年12月08日 | 2019年04月29日 | "Swollen" |
    | "Jack Stone" | 2003年12月08日 | 2018年09月02日 | "Scratchy" |
    | "Jack Stone" | 2003年12月08日 | 2019年07月06日 | "Itchy" |
    | "Jack Stone" | 2003年12月08日 | 2019年07月06日 | "Itchy" |
    | "Jack Stone" | 2003年12月08日 | 2018年04月16日 | "Pale" |
    | "Jack Stone" | 2003年12月08日 | 2018年02月10日 | "Sore" |
    | "Mary Anderson" | 1974年07月22日 | 2018年06月08日 | "Dizzy" |
    | "Mary Anderson" | 1974年07月22日 | 2018年06月08日 | "Dizzy" |
    | "Mary Anderson" | 1974年07月22日 | 2019年12月02日 | "Pale" |
    | "Mary Anderson" | 1974年07月22日 | 2018年09月08日 | "Angry" |
    | "Mary Anderson" | 1974年07月22日 | 2018年07月05日 | "Swollen" |
    | "Mary Anderson" | 1974年07月22日 | 2018年03月08日 | "Itchy" |
    | "Sally Jackson" | 1994年02月20日 | 2019年07月19日 | "Dizzy" |
    | "Sally Jackson" | 1994年02月20日 | 2019年06月29日 | "Pale" |
    | "Sally Jackson" | 1994年02月20日 | 2019年06月14日 | "Angry" |
    | "Sally Jackson" | 1994年02月20日 | 2018年07月27日 | "Red" |
    | "Sally Jackson" | 1994年02月20日 | 2019年01月21日 | "Sad" |
    | "Sally Jackson" | 1994年02月20日 | 2018年10月25日 | "Swollen" |
    | "Mark Svensson" | 1985年09月07日 | 2018年01月06日 | "Inflamed" |
    | "Mark Svensson" | 1985年09月07日 | 2018年01月06日 | "Inflamed" |
    | "Mark Svensson" | 1985年09月07日 | 2019年04月04日 | "Red" |
    | "Mark Svensson" | 1985年09月07日 | 2018年04月02日 | "Angry" |
    | "Mark Svensson" | 1985年09月07日 | 2018年11月12日 | "Itchy" |
    | "Mark Svensson" | 1985年09月07日 | 2018年11月12日 | "Itchy" |
    | "Joe Smith" | 1980年06月17日 | 2018年10月23日 | "Sore" |
    | "Joe Smith" | 1980年06月17日 | 2018年03月19日 | "Angry" |
    | "Joe Smith" | 1980年06月17日 | 2018年03月19日 | "Angry" |
    | "Joe Smith" | 1980年06月17日 | 2019年02月12日 | "Itchy" |
    | "Joe Smith" | 1980年06月17日 | 2019年02月12日 | "Itchy" |
    | "Joe Smith" | 1980年06月17日 | 2019年09月17日 | "Pale" |
    | "Jane Stone" | 2015年11月26日 | 2019年04月27日 | "Scratchy" |
    | "Jane Stone" | 2015年11月26日 | 2017年09月21日 | "Itchy" |
    | "Jane Stone" | 2015年11月26日 | 2017年07月18日 | "Inflamed" |
    | "Jane Stone" | 2015年11月26日 | 2018年04月05日 | "Pale" |
    | "Jane Stone" | 2015年11月26日 | 2017年07月18日 | "Inflamed" |
    | "Jane Stone" | 2015年11月26日 | 2019年01月12日 | "Swollen" |
    | "Bob Anderson" | 2007年02月06日 | 2018年08月16日 | "Itchy" |
    | "Bob Anderson" | 2007年02月06日 | 2019年08月23日 | "Inflamed" |
    | "Bob Anderson" | 2007年02月06日 | 2018年08月31日 | "Dizzy" |
    | "Bob Anderson" | 2007年02月06日 | 2019年01月16日 | "Sore" |
    | "Bob Anderson" | 2007年02月06日 | 2018年08月31日 | "Dizzy" |
    | "Bob Anderson" | 2007年02月06日 | 2018年02月14日 | "Angry" |
    | "Ally Jackson" | 1997年12月29日 | 2018年03月10日 | "Pale" |
    | "Ally Jackson" | 1997年12月29日 | 2019年11月21日 | "Red" |
    | "Ally Jackson" | 1997年12月29日 | 2018年03月10日 | "Pale" |
    | "Ally Jackson" | 1997年12月29日 | 2019年11月21日 | "Red" |
    | "Ally Jackson" | 1997年12月29日 | 2018年09月20日 | "Dizzy" |
    | "Ally Jackson" | 1997年12月29日 | 2018年03月12日 | "Itchy" |
    | "Jack Svensson" | 1974年04月26日 | 2018年07月04日 | "Sore" |
    | "Jack Svensson" | 1974年04月26日 | 2019年06月15日 | "Angry" |
    | "Jack Svensson" | 1974年04月26日 | 2019年09月04日 | "Inflamed" |
    | "Jack Svensson" | 1974年04月26日 | 2017年08月12日 | "Swollen" |
    | "Jack Svensson" | 1974年04月26日 | 2018年07月04日 | "Sore" |
    | "Jack Svensson" | 1974年04月26日 | 2019年11月06日 | "Itchy" |
    | "Mary Smith" | 2007年06月30日 | 2018年06月13日 | "Sad" |
    | "Mary Smith" | 2007年06月30日 | 2019年06月21日 | "Itchy" |
    | "Mary Smith" | 2007年06月30日 | 2019年02月04日 | "Dizzy" |
    | "Mary Smith" | 2007年06月30日 | 2018年03月15日 | "Angry" |
    | "Mary Smith" | 2007年06月30日 | 2018年03月15日 | "Angry" |
    | "Mary Smith" | 2007年06月30日 | 2018年12月07日 | "Sore" |
    | "Sally Stone" | 1999年06月21日 | 2018年01月05日 | "Sore" |
    | "Sally Stone" | 1999年06月21日 | 2018年02月19日 | "Angry" |
    | "Sally Stone" | 1999年06月21日 | 2018年01月05日 | "Sore" |
    | "Sally Stone" | 1999年06月21日 | 2018年01月05日 | "Sore" |
    | "Sally Stone" | 1999年06月21日 | 2018年04月08日 | "Scratchy" |
    | "Sally Stone" | 1999年06月21日 | 2018年01月18日 | "Dizzy" |
    | "Mark Anderson" | 1995年09月26日 | 2018年03月04日 | "Scratchy" |
    | "Mark Anderson" | 1995年09月26日 | 2018年10月14日 | "Sad" |
    | "Mark Anderson" | 1995年09月26日 | 2019年05月16日 | "Pale" |
    | "Mark Anderson" | 1995年09月26日 | 2017年12月09日 | "Swollen" |
    | "Mark Anderson" | 1995年09月26日 | 2019年04月17日 | "Inflamed" |
    | "Mark Anderson" | 1995年09月26日 | 2019年05月16日 | "Pale" |
    | "Joe Jackson" | 1973年06月05日 | 2019年01月03日 | "Scratchy" |
    | "Joe Jackson" | 1973年06月05日 | 2018年08月29日 | "Itchy" |
    | "Joe Jackson" | 1973年06月05日 | 2019年07月06日 | "Angry" |
    | "Joe Jackson" | 1973年06月05日 | 2018年08月29日 | "Itchy" |
    | "Joe Jackson" | 1973年06月05日 | 2018年04月21日 | "Dizzy" |
    | "Joe Jackson" | 1973年06月05日 | 2018年06月21日 | "Sore" |
    | "Jane Svensson" | 1970年05月24日 | 2018年05月03日 | "Sore" |
    | "Jane Svensson" | 1970年05月24日 | 2019年02月12日 | "Inflamed" |
    | "Jane Svensson" | 1970年05月24日 | 2018年04月18日 | "Angry" |
    | "Jane Svensson" | 1970年05月24日 | 2019年04月12日 | "Swollen" |
    | "Jane Svensson" | 1970年05月24日 | 2018年12月08日 | "Red" |
    | "Jane Svensson" | 1970年05月24日 | 2017年11月17日 | "Itchy" |
    | "Bob Smith" | 2014年07月07日 | 2018年04月05日 | "Dizzy" |
    | "Bob Smith" | 2014年07月07日 | 2018年01月21日 | "Red" |
    | "Bob Smith" | 2014年07月07日 | 2018年04月05日 | "Dizzy" |
    | "Bob Smith" | 2014年07月07日 | 2018年04月05日 | "Dizzy" |
    | "Bob Smith" | 2014年07月07日 | 2018年02月04日 | "Sad" |
    | "Bob Smith" | 2014年07月07日 | 2019年05月01日 | "Pale" |
    | "Ally Stone" | 1994年08月11日 | 2017年10月17日 | "Inflamed" |
    | "Ally Stone" | 1994年08月11日 | 2017年08月20日 | "Red" |
    | "Ally Stone" | 1994年08月11日 | 2017年10月17日 | "Inflamed" |
    | "Ally Stone" | 1994年08月11日 | 2019年03月15日 | "Angry" |
    | "Ally Stone" | 1994年08月11日 | 2019年03月15日 | "Angry" |
    | "Ally Stone" | 1994年08月11日 | 2018年08月26日 | "Swollen" |
    | "Jack Anderson" | 1994年08月22日 | 2017年09月25日 | "Inflamed" |
    | "Jack Anderson" | 1994年08月22日 | 2019年10月18日 | "Sad" |
    | "Jack Anderson" | 1994年08月22日 | 2018年11月12日 | "Swollen" |
    | "Jack Anderson" | 1994年08月22日 | 2019年10月18日 | "Sad" |
    | "Jack Anderson" | 1994年08月22日 | 2018年11月12日 | "Swollen" |
    | "Jack Anderson" | 1994年08月22日 | 2018年09月29日 | "Sore" |
    | "Mary Jackson" | 1993年02月17日 | 2017年07月18日 | "Scratchy" |
    | "Mary Jackson" | 1993年02月17日 | 2019年09月15日 | "Red" |
    | "Mary Jackson" | 1993年02月17日 | 2018年05月28日 | "Itchy" |
    | "Mary Jackson" | 1993年02月17日 | 2018年05月28日 | "Itchy" |
    | "Mary Jackson" | 1993年02月17日 | 2018年09月28日 | "Inflamed" |
    | "Mary Jackson" | 1993年02月17日 | 2017年08月19日 | "Sad" |
    | "Sally Svensson" | 2015年04月19日 | 2017年11月15日 | "Sad" |
    | "Sally Svensson" | 2015年04月19日 | 2018年07月30日 | "Sore" |
    | "Sally Svensson" | 2015年04月19日 | 2017年11月15日 | "Sad" |
    | "Sally Svensson" | 2015年04月19日 | 2018年10月05日 | "Pale" |
    | "Sally Svensson" | 2015年04月19日 | 2019年06月14日 | "Dizzy" |
    | "Sally Svensson" | 2015年04月19日 | 2018年03月09日 | "Scratchy" |
    | "Mark Smith" | 2012年08月01日 | 2018年09月27日 | "Swollen" |
    | "Mark Smith" | 2012年08月01日 | 2018年06月25日 | "Angry" |
    | "Mark Smith" | 2012年08月01日 | 2019年01月08日 | "Sore" |
    | "Mark Smith" | 2012年08月01日 | 2018年09月20日 | "Pale" |
    | "Mark Smith" | 2012年08月01日 | 2019年09月30日 | "Scratchy" |
    | "Mark Smith" | 2012年08月01日 | 2018年09月20日 | "Pale" |
    | "Joe Stone" | 2003年12月31日 | 2018年01月29日 | "Sore" |
    | "Joe Stone" | 2003年12月31日 | 2017年10月01日 | "Angry" |
    | "Joe Stone" | 2003年12月31日 | 2019年07月16日 | "Scratchy" |
    | "Joe Stone" | 2003年12月31日 | 2018年03月03日 | "Red" |
    | "Joe Stone" | 2003年12月31日 | 2017年10月14日 | "Dizzy" |
    | "Joe Stone" | 2003年12月31日 | 2017年08月18日 | "Pale" |
    | "Jane Anderson" | 2010年06月04日 | 2019年12月02日 | "Sore" |
    | "Jane Anderson" | 2010年06月04日 | 2018年03月24日 | "Scratchy" |
    | "Jane Anderson" | 2010年06月04日 | 2018年07月20日 | "Sad" |
    | "Jane Anderson" | 2010年06月04日 | 2019年06月17日 | "Swollen" |
    | "Jane Anderson" | 2010年06月04日 | 2018年12月21日 | "Red" |
    | "Jane Anderson" | 2010年06月04日 | 2019年12月02日 | "Sore" |
    | "Bob Jackson" | 1979年11月07日 | 2018年05月03日 | "Sore" |
    | "Bob Jackson" | 1979年11月07日 | 2018年04月20日 | "Angry" |
    | "Bob Jackson" | 1979年11月07日 | 2018年05月10日 | "Pale" |
    | "Bob Jackson" | 1979年11月07日 | 2018年02月09日 | "Swollen" |
    | "Bob Jackson" | 1979年11月07日 | 2019年10月14日 | "Scratchy" |
    | "Bob Jackson" | 1979年11月07日 | 2018年04月23日 | "Dizzy" |
    | "Ally Svensson" | 2004年12月14日 | 2019年03月13日 | "Scratchy" |
    | "Ally Svensson" | 2004年12月14日 | 2019年03月13日 | "Scratchy" |
    | "Ally Svensson" | 2004年12月14日 | 2018年02月22日 | "Inflamed" |
    | "Ally Svensson" | 2004年12月14日 | 2018年05月16日 | "Dizzy" |
    | "Ally Svensson" | 2004年12月14日 | 2018年07月09日 | "Pale" |
    | "Ally Svensson" | 2004年12月14日 | 2019年04月30日 | "Itchy" |
    | "Jack Smith" | 2010年09月23日 | 2018年11月27日 | "Angry" |
    | "Jack Smith" | 2010年09月23日 | 2018年04月18日 | "Pale" |
    | "Jack Smith" | 2010年09月23日 | 2019年03月05日 | "Itchy" |
    | "Jack Smith" | 2010年09月23日 | 2018年09月29日 | "Sore" |
    | "Jack Smith" | 2010年09月23日 | 2019年02月08日 | "Red" |
    | "Jack Smith" | 2010年09月23日 | 2019年02月22日 | "Sad" |
    | "Mary Stone" | 2009年07月14日 | 2017年11月22日 | "Pale" |
    | "Mary Stone" | 2009年07月14日 | 2018年09月21日 | "Scratchy" |
    | "Mary Stone" | 2009年07月14日 | 2018年09月30日 | "Angry" |
    | "Mary Stone" | 2009年07月14日 | 2019年06月17日 | "Itchy" |
    | "Mary Stone" | 2009年07月14日 | 2017年11月22日 | "Pale" |
    | "Mary Stone" | 2009年07月14日 | 2019年08月14日 | "Sore" |
    | "Sally Anderson" | 1972年01月23日 | 2017年09月13日 | "Dizzy" |
    | "Sally Anderson" | 1972年01月23日 | 2018年09月09日 | "Itchy" |
    | "Sally Anderson" | 1972年01月23日 | 2018年06月23日 | "Sad" |
    | "Sally Anderson" | 1972年01月23日 | 2018年06月23日 | "Sad" |
    | "Sally Anderson" | 1972年01月23日 | 2019年12月03日 | "Sore" |
    | "Sally Anderson" | 1972年01月23日 | 2019年12月03日 | "Sore" |
    | "Mark Jackson" | 2006年12月17日 | 2018年08月02日 | "Scratchy" |
    | "Mark Jackson" | 2006年12月17日 | 2018年01月28日 | "Sore" |
    | "Mark Jackson" | 2006年12月17日 | 2018年08月02日 | "Scratchy" |
    | "Mark Jackson" | 2006年12月17日 | 2018年05月26日 | "Itchy" |
    | "Mark Jackson" | 2006年12月17日 | 2017年11月27日 | "Sad" |
    | "Mark Jackson" | 2006年12月17日 | 2017年07月17日 | "Red" |
    | "Joe Svensson" | 2001年01月14日 | 2019年02月20日 | "Dizzy" |
    | "Joe Svensson" | 2001年01月14日 | 2018年07月12日 | "Angry" |
    | "Joe Svensson" | 2001年01月14日 | 2018年08月08日 | "Itchy" |
    | "Joe Svensson" | 2001年01月14日 | 2018年03月30日 | "Pale" |
    | "Joe Svensson" | 2001年01月14日 | 2019年02月20日 | "Dizzy" |
    | "Joe Svensson" | 2001年01月14日 | 2018年03月30日 | "Pale" |
    | "Jane Smith" | 1992年03月12日 | 2017年10月07日 | "Dizzy" |
    | "Jane Smith" | 1992年03月12日 | 2019年03月07日 | "Inflamed" |
    | "Jane Smith" | 1992年03月12日 | 2019年05月30日 | "Red" |
    | "Jane Smith" | 1992年03月12日 | 2017年07月15日 | "Pale" |
    | "Jane Smith" | 1992年03月12日 | 2019年04月14日 | "Sore" |
    | "Jane Smith" | 1992年03月12日 | 2019年09月30日 | "Angry" |
    | "Bob Stone" | 2015年09月07日 | 2018年09月11日 | "Sore" |
    | "Bob Stone" | 2015年09月07日 | 2018年03月19日 | "Dizzy" |
    | "Bob Stone" | 2015年09月07日 | 2019年01月01日 | "Scratchy" |
    | "Bob Stone" | 2015年09月07日 | 2019年01月01日 | "Scratchy" |
    | "Bob Stone" | 2015年09月07日 | 2017年12月27日 | "Sad" |
    | "Bob Stone" | 2015年09月07日 | 2019年01月01日 | "Scratchy" |
    | "Ally Anderson" | 1978年12月20日 | 2018年08月20日 | "Inflamed" |
    | "Ally Anderson" | 1978年12月20日 | 2018年03月23日 | "Sad" |
    | "Ally Anderson" | 1978年12月20日 | 2017年09月05日 | "Itchy" |
    | "Ally Anderson" | 1978年12月20日 | 2018年01月07日 | "Angry" |
    | "Ally Anderson" | 1978年12月20日 | 2018年02月08日 | "Pale" |
    | "Ally Anderson" | 1978年12月20日 | 2018年02月08日 | "Pale" |
    | "Jack Jackson" | 1975年06月08日 | 2018年10月10日 | "Red" |
    | "Jack Jackson" | 1975年06月08日 | 2019年09月16日 | "Pale" |
    | "Jack Jackson" | 1975年06月08日 | 2019年09月16日 | "Pale" |
    | "Jack Jackson" | 1975年06月08日 | 2019年09月16日 | "Pale" |
    | "Jack Jackson" | 1975年06月08日 | 2019年01月06日 | "Angry" |
    | "Jack Jackson" | 1975年06月08日 | 2018年02月21日 | "Scratchy" |
    | "Mary Svensson" | 2002年01月09日 | 2018年09月24日 | "Pale" |
    | "Mary Svensson" | 2002年01月09日 | 2018年03月03日 | "Itchy" |
    | "Mary Svensson" | 2002年01月09日 | 2017年12月29日 | "Swollen" |
    | "Mary Svensson" | 2002年01月09日 | 2019年11月01日 | "Sad" |
    | "Mary Svensson" | 2002年01月09日 | 2018年03月03日 | "Itchy" |
    | "Mary Svensson" | 2002年01月09日 | 2018年03月03日 | "Itchy" |
    | "Sally Smith" | 1973年12月03日 | 2018年01月06日 | "Itchy" |
    | "Sally Smith" | 1973年12月03日 | 2018年01月06日 | "Itchy" |
    | "Sally Smith" | 1973年12月03日 | 2019年02月13日 | "Dizzy" |
    | "Sally Smith" | 1973年12月03日 | 2019年10月21日 | "Pale" |
    | "Sally Smith" | 1973年12月03日 | 2017年10月26日 | "Scratchy" |
    | "Sally Smith" | 1973年12月03日 | 2017年12月21日 | "Red" |
    | "Mark Stone" | 1988年06月07日 | 2018年06月07日 | "Swollen" |
    | "Mark Stone" | 1988年06月07日 | 2019年09月06日 | "Scratchy" |
    | "Mark Stone" | 1988年06月07日 | 2018年08月09日 | "Itchy" |
    | "Mark Stone" | 1988年06月07日 | 2019年09月06日 | "Scratchy" |
    | "Mark Stone" | 1988年06月07日 | 2019年06月12日 | "Dizzy" |
    | "Mark Stone" | 1988年06月07日 | 2019年09月06日 | "Scratchy" |
    | "Joe Anderson" | 2010年10月05日 | 2018年01月05日 | "Pale" |
    | "Joe Anderson" | 2010年10月05日 | 2017年12月26日 | "Scratchy" |
    | "Joe Anderson" | 2010年10月05日 | 2018年01月05日 | "Pale" |
    | "Joe Anderson" | 2010年10月05日 | 2019年04月11日 | "Inflamed" |
    | "Joe Anderson" | 2010年10月05日 | 2019年01月02日 | "Sore" |
    | "Joe Anderson" | 2010年10月05日 | 2019年02月28日 | "Dizzy" |
    +------------------------------------------------------------+
    606 rows
    ready to start consuming query after 91 ms, results consumed after another 60 ms
    Created 491 relationships, Set 491 properties

Manage authorization and access control

Unlike applications which often require users to be modeled within the application itself, databases provide user management resources such as roles and privileges. This allows users to be created entirely within the database security model, a strategy that allows the separation of access to the data and the data itself. For more information, see Authentication and authorization.

In this tutorial, consider five users of the healthcare database:

  • Alice, the doctor.

  • Daniel, the nurse.

  • Bob, the receptionist.

  • Charlie, the researcher.

  • Tina, the IT administrator.

You can create these users by using the CREATE USER command (from the system database):

CREATE USER charlie SET PASSWORD 'secretpassword1' CHANGE NOT REQUIRED;
CREATE USER alice SET PASSWORD 'secretpassword2' CHANGE NOT REQUIRED;
CREATE USER daniel SET PASSWORD 'secretpassword3' CHANGE NOT REQUIRED;
CREATE USER bob SET PASSWORD 'secretpassword4' CHANGE NOT REQUIRED;
CREATE USER tina SET PASSWORD 'secretpassword5' CHANGE NOT REQUIRED;

At this point, the users cannot interact with the database, so these capabilities need to be granted by using roles. There are two different ways of doing this either by using built-in roles and privileges or by using more advanced resources with fine-grained privileges for sub-graph access control.

Access control using built-in roles

Neo4j comes with built-in roles that cover a number of common needs:

  • PUBLIC - All users have this role. They can by default access the home database, load data, and run all procedures and user-defined functions.

  • reader - Can read data from all databases.

  • editor - Can read and update all databases, but not expand the schema with new labels, relationship types, or property names.

  • publisher - Can read and edit, as well as add new labels, relationship types, and property names.

  • architect - Has all the capabilities of the publisher as well as the ability to manage indexes and constraints.

  • admin - Can perform architect actions as well as load data and manage databases, users, roles, and privileges.

Consider Charlie from the example of users. As a researcher, they do not need write access to the database, so they are assigned the reader role.

On the other hand, Alice (the doctor), Daniel (the nurse), and Bob (the receptionist) all need to update the database with new patient information but do not need to expand the schema with new labels, relationship types, property names, or indexes. For this reason, they are all assigned the editor role.

Tina, the IT administrator who installs and manages the database, needs to be assigned the admin role.

Here is how to grant roles to the users (from the system database):

GRANT ROLE reader TO charlie;
GRANT ROLE editor TO alice;
GRANT ROLE editor TO daniel;
GRANT ROLE editor TO bob;
GRANT ROLE admin TO tina;

Sub-graph access control using privileges

A limitation of the previously described approach is that it does allow all users to see all the data on the database. In many real-world scenarios though, it would be preferable to establish some access restrictions.

For example, you may want to limit the researcher’s access to the patients' personal information or restrict the receptionist from writing new labels on the database. While these restrictions could be coded into the application layer, it is possible and more secure to enforce fine-grained restrictions directly within the Neo4j security model by creating custom roles and assigning specific privileges to them.

Since new custom roles will be created, it is important to first revoke the current roles from the users assigned to them. Run the following command against the system database:

REVOKE ROLE reader FROM charlie;
REVOKE ROLE editor FROM alice;
REVOKE ROLE editor FROM daniel;
REVOKE ROLE editor FROM bob;
REVOKE ROLE admin FROM tina;

Now you can create custom roles based on the concept of privileges, which allows more control over what each user is capable of doing. To properly assign those privileges, start by identifying each type of user:

Doctor

Should be able to read and write most of the graph, but be prevented from reading the patients' address. Has the permission to save diagnoses to the database, but not expand the schema with new concepts.

Receptionist

Should be able to read and write all patient data, but not be able to see the symptoms, diseases, or diagnoses.

Researcher

Should be able to perform statistical analysis of all data, except patients’ personal information, to which they should have restricted access. To illustrate two different ways of setting up the same effective privileges, two roles are created for comparison.

Nurse

Should be able to perform all tasks that both the doctor and the receptionist can do. Granting both roles (doctor and receptionist) to the nurse does not work as expected. This is explained in the section dedicated to the creation of the nurse role.

Junior nurse

While the senior nurse is able to save diagnoses just as a doctor can, some (junior) nurses might not be allowed to do that. Creating another role from scratch is an option, but the same output can be achieved by combining the nurse role with a new disableDiagnoses role that specifically restricts that activity.

IT administrator

This role is very similar to the built-in admin role, except that it should not allow access to the patients' SSN or be able to save a diagnosis, a privilege restricted to medical professionals. To achieve this, the built-in admin role can be copied and modified accordingly.

User manager

This user should have similar access as the IT administrator, but with more restrictions. To achieve that, a new role can be created from scratch and only specific administrative capabilities can be assigned to it.

Before creating the new roles and assigning them to Alice, Bob, Daniel, Charlie, and Tina, it is important to define the privileges each role should have. Since all users need ACCESS privilege to the healthcare database, this can be set through the PUBLIC role instead of all the individual roles:

Run the following command against the system database:

GRANT ACCESS ON DATABASE healthcare TO PUBLIC;

Privileges of itadmin

This role can be created as a copy of the built-in admin role:

CREATE ROLE itadmin AS COPY OF admin;

Then you need to deny the two specific actions this role is not supposed to perform:

  • Read any patients' social security number (SSN).

  • Submit medical diagnoses.

As well as the ability for the itadmin to amend their own privileges.

DENY READ {ssn} ON GRAPH healthcare NODES Patient TO itadmin;
DENY CREATE ON GRAPH healthcare RELATIONSHIPS DIAGNOSIS TO itadmin;
DENY ROLE MANAGEMENT ON DBMS TO itadmin;
DENY PRIVILEGE MANAGEMENT ON DBMS TO itadmin;

The complete set of privileges available to users assigned the itadmin role can be viewed using the following command:

SHOW ROLE itadmin PRIVILEGES AS COMMANDS;
Result
+-------------------------------------------------------------------------+
| command |
+-------------------------------------------------------------------------+
| "DENY CREATE ON GRAPH `healthcare` RELATIONSHIP DIAGNOSIS TO `itadmin`" |
| "DENY PRIVILEGE MANAGEMENT ON DBMS TO `itadmin`" |
| "DENY READ {ssn} ON GRAPH `healthcare` NODE Patient TO `itadmin`" |
| "DENY ROLE MANAGEMENT ON DBMS TO `itadmin`" |
| "GRANT ACCESS ON DATABASE * TO `itadmin`" |
| "GRANT ALL DBMS PRIVILEGES ON DBMS TO `itadmin`" |
| "GRANT CONSTRAINT MANAGEMENT ON DATABASE * TO `itadmin`" |
| "GRANT INDEX MANAGEMENT ON DATABASE * TO `itadmin`" |
| "GRANT LOAD ON ALL DATA TO `itadmin`" |
| "GRANT MATCH {*} ON GRAPH * NODE * TO `itadmin`" |
| "GRANT MATCH {*} ON GRAPH * RELATIONSHIP * TO `itadmin`" |
| "GRANT NAME MANAGEMENT ON DATABASE * TO `itadmin`" |
| "GRANT SHOW CONSTRAINT ON DATABASE * TO `itadmin`" |
| "GRANT SHOW INDEX ON DATABASE * TO `itadmin`" |
| "GRANT START ON DATABASE * TO `itadmin`" |
| "GRANT STOP ON DATABASE * TO `itadmin`" |
| "GRANT TRANSACTION MANAGEMENT (*) ON DATABASE * TO `itadmin`" |
| "GRANT WRITE ON GRAPH * TO `itadmin`" |
+-------------------------------------------------------------------------+
18 rows
ready to start consuming query after 29 ms, results consumed after another 1 ms

Privileges that were granted or denied earlier can be revoked using the REVOKE command.

To provide the IT administrator tina these privileges, they must be assigned the new role itadmin. Run the following command against the system database:

GRANT ROLE itadmin TO tina;

To demonstrate that Tina is not able to see the patients' SSN, you can log into Cypher Shell as tina and run the following query against the healthcare database:

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
Result
+---------------------------------------------------------------------+
| n.name | n.ssn | n.address | n.dateOfBirth |
+---------------------------------------------------------------------+
| "Mark Jackson" | NULL | "1 secret way, downtown" | 1970年11月29日 |
| "Joe Svensson" | NULL | "1 secret way, downtown" | 1972年02月12日 |
| "Bob Anderson" | NULL | "1 secret way, downtown" | 1970年04月27日 |
| "Sally Anderson" | NULL | "1 secret way, downtown" | 1970年12月02日 |
| "Ally Anderson" | NULL | "1 secret way, downtown" | 1972年05月20日 |
| "Jane Svensson" | NULL | "1 secret way, downtown" | 1970年05月24日 |
| "Sally Anderson" | NULL | "1 secret way, downtown" | 1972年01月23日 |
+---------------------------------------------------------------------+
7 rows
ready to start consuming query after 49 ms, results consumed after another 2 ms

The results make it seem as if these nodes do not even have an SSN field. This is a key feature of the security model: users cannot tell the difference between data that does not exist and data that is hidden using fine-grained read privileges.

Now recall that the itadmin role was denied the ability to save diagnoses (as this is a critical medical function reserved for only doctors and senior medical staff), you can test this by trying to create DIAGNOSIS relationships:

MATCH (n:Patient), (d:Disease)
CREATE (n)-[:DIAGNOSIS]->(d);
Result
Create relationship with type 'DIAGNOSIS' on database 'healthcare' is not allowed for user 'tina' with roles [PUBLIC, itadmin].

Restrictions to reading data do not result in errors, they only make it appear as if the data is not there. However, restrictions on updating the graph do output an appropriate error when the user attempts to perform an action they are not allowed to.

Privileges of researcher

The researcher Charlie was previously a read-only user. To assign them the desired permissions, you can do something similar to what was done with the itadmin role, this time copying and modifying the reader role.

Another way to do it is by creating a new role from scratch and then either granting or denying a list of privileges:

  • Denying privileges:

    You can grant the role researcher the ability to find all nodes and read all properties (much like the reader role), but deny read access to the Patient properties. This way, the researcher is unable to see patients' information such as name, SSN, and address. This approach has a problem though: if more properties are added to the Patient nodes after the restrictions were assigned to the researcher role, these new properties will automatically be visible to the researcher — a possibly undesirable outcome.

    To avoid that, you can rather deny specific privileges by running the following commands against the system database. You must be logged in as a user with the admin role to be able to execute these commands:

    // First create the role
    CREATE ROLE researcherB;
    // Then grant access to everything
    GRANT MATCH {*}
     ON GRAPH healthcare
     TO researcherB;
    // And deny read on specific node properties
    DENY READ {name, address, ssn}
     ON GRAPH healthcare
     NODES Patient
     TO researcherB;
    // And finally deny traversal of the doctors diagnosis
    DENY TRAVERSE
     ON GRAPH healthcare
     RELATIONSHIPS DIAGNOSIS
     TO researcherB;
  • Granting privileges:

    Another alternative is to only provide specific access to the properties the researcher is allowed to see. This way, the addition of new properties (for instance, to a Patient node) does not automatically make them visible to users assigned with this role. In case you wish to make them visible though, you need to explicitly grant read access. Run the following commands against the system database. You must be logged in as a user with the admin role to be able to execute these commands:

    // Create the role first
    CREATE ROLE researcherW
    // Allow the researcher to find all nodes
    GRANT TRAVERSE
     ON GRAPH healthcare
     NODES *
     TO researcherW;
    // Now only allow the researcher to traverse specific relationships
    GRANT TRAVERSE
     ON GRAPH healthcare
     RELATIONSHIPS HAS, OF
     TO researcherW;
    // Allow reading of all properties of medical metadata
    GRANT READ {*}
     ON GRAPH healthcare
     NODES Symptom, Disease
     TO researcherW;
    // Allow reading of all properties of the disease-symptom relationship
    GRANT READ {*}
     ON GRAPH healthcare
     RELATIONSHIPS OF
     TO researcherW;
    // Only allow reading dateOfBirth for research purposes
    GRANT READ {dateOfBirth}
     ON GRAPH healthcare
     NODES Patient
     TO researcherW;

    In order to test that the researcher Charlie now has the specified privileges, assign them the researcherB role (with specifically denied privileges):

    GRANT ROLE researcherB TO charlie;

    You can also use a version of the SHOW PRIVILEGES command to see Charlie’s access rights, which are a combination of those assigned to the researcherB and PUBLIC roles:

    SHOW USER charlie PRIVILEGES AS COMMANDS;
    Result
    +-----------------------------------------------------------------------+
    | command |
    +-----------------------------------------------------------------------+
    | "DENY READ {address} ON GRAPH `healthcare` NODE Patient TO $role" |
    | "DENY READ {name} ON GRAPH `healthcare` NODE Patient TO $role" |
    | "DENY READ {ssn} ON GRAPH `healthcare` NODE Patient TO $role" |
    | "DENY TRAVERSE ON GRAPH `healthcare` RELATIONSHIP DIAGNOSIS TO $role" |
    | "GRANT ACCESS ON DATABASE `healthcare` TO $role" |
    | "GRANT ACCESS ON HOME DATABASE TO $role" |
    | "GRANT EXECUTE FUNCTION * ON DBMS TO $role" |
    | "GRANT EXECUTE PROCEDURE * ON DBMS TO $role" |
    | "GRANT LOAD ON ALL DATA TO $role" |
    | "GRANT MATCH {*} ON GRAPH `healthcare` NODE * TO $role" |
    | "GRANT MATCH {*} ON GRAPH `healthcare` RELATIONSHIP * TO $role" |
    +-----------------------------------------------------------------------+
    11 rows
    ready to start consuming query after 17 ms, results consumed after another 2 ms

    Now when Charlie logs into Cypher Shell and tries to execute the following command against the healthcare database, even though the command is similar to the one previously used by the itadmin, they will see different results:

    MATCH (n:Patient)
     WHERE n.dateOfBirth < date('1972-06-12')
    RETURN n.name, n.ssn, n.address, n.dateOfBirth;
    Result
    +--------------------------------------------+
    | n.name | n.ssn | n.address | n.dateOfBirth |
    +--------------------------------------------+
    | NULL | NULL | NULL | 1970年11月29日 |
    | NULL | NULL | NULL | 1972年02月12日 |
    | NULL | NULL | NULL | 1970年04月27日 |
    | NULL | NULL | NULL | 1970年12月02日 |
    | NULL | NULL | NULL | 1972年05月20日 |
    | NULL | NULL | NULL | 1970年05月24日 |
    | NULL | NULL | NULL | 1972年01月23日 |
    +--------------------------------------------+
    7 rows
    ready to start consuming query after 5 ms, results consumed after another 4 ms

    Only the date of birth is available, so that the researcher Charlie may perform statistical analysis, for example. Another query Charlie could try is to find the ten diseases a patient younger than 25 is most likely to be diagnosed with, listed by probability:

    WITH datetime() - duration({years:25}) AS timeLimit
    MATCH (n:Patient)
    WHERE n.dateOfBirth > date(timeLimit)
    MATCH (n)-[h:HAS]->(s:Symptom)-[o:OF]->(d:Disease)
    WITH d.name AS disease, o.probability AS prob
    RETURN disease, sum(prob) AS score ORDER BY score DESC LIMIT 10;
    Result
    +-------------------------------------------+
    | disease | score |
    +-------------------------------------------+
    | "Acute Placeboitis" | 98.08269474672981 |
    | "Chronic Whatitis" | 92.7601237335886 |
    | "Acute Otheritis" | 87.61578906815608 |
    | "Chronic Someitis" | 81.68350008637253 |
    | "Chronic Placeboitis" | 81.18800771016768 |
    | "Acute Argitis" | 80.94323685188083 |
    | "Chronic Argitis" | 80.06685163653665 |
    | "Chronic Otheritis" | 76.06538667789484 |
    | "Acute Yellowitis" | 70.74589062185173 |
    | "Acute Someitis" | 70.3238679154795 |
    +-------------------------------------------+
    10 rows
    ready to start consuming query after 171 ms, results consumed after another 23 ms

    If the researcherB role is revoked to Charlie, but researcherW is granted, when re-running these queries, the same results will be obtained.

    Privileges that were granted or denied earlier can be revoked using the REVOKE command.

Privileges of doctor

Doctors should be given the ability to read and write almost everything, except the patients' address property, for instance. This role can be built from scratch by assigning full read and write access, and then specifically denying access to the address property. Switch to the system database and run the following commands:

CREATE ROLE doctor;
GRANT TRAVERSE ON GRAPH healthcare TO doctor;
GRANT READ {*} ON GRAPH healthcare TO doctor;
GRANT WRITE ON GRAPH healthcare TO doctor;
DENY READ {address} ON GRAPH healthcare NODES Patient TO doctor;
DENY SET PROPERTY {address} ON GRAPH healthcare NODES Patient TO doctor;

To allow the doctor Alice to have these privileges, grant the user alice this new role:

GRANT ROLE doctor TO alice;

To demonstrate that Alice is not able to see patient addresses, log in as alice and run the following query against the healthcare database:

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
Result
+--------------------------------------------------------+
| n.name | n.ssn | n.address | n.dateOfBirth |
+--------------------------------------------------------+
| "Mark Jackson" | 1234578 | NULL | 1970年11月29日 |
| "Joe Svensson" | 1234579 | NULL | 1972年02月12日 |
| "Bob Anderson" | 1234597 | NULL | 1970年04月27日 |
| "Sally Anderson" | 1234617 | NULL | 1970年12月02日 |
| "Ally Anderson" | 1234622 | NULL | 1972年05月20日 |
| "Jane Svensson" | 1234644 | NULL | 1970年05月24日 |
| "Sally Anderson" | 1234657 | NULL | 1972年01月23日 |
+--------------------------------------------------------+
7 rows
ready to start consuming query after 5 ms, results consumed after another 3 ms

As a result, the doctor has the expected privileges, including being able to see the patients' SSN, but not their address.

The doctor is also able to see all other node types:

MATCH (n) WITH labels(n) AS labels
RETURN labels, count(*);
Result
+------------------------+
| labels | count(*) |
+------------------------+
| ["Symptom"] | 10 |
| ["Disease"] | 12 |
| ["Patient"] | 101 |
+------------------------+
3 rows
ready to start consuming query after 29 ms, results consumed after another 1 ms

In addition, the doctor can traverse the graph, finding symptoms and diseases connected to patients:

MATCH (n:Patient)-[:HAS]->(s:Symptom)-[:OF]->(d:Disease)
 WHERE n.ssn = 1234657
RETURN n.name, d.name, count(s) AS score ORDER BY score DESC;

The resulting table shows which are the most likely diagnoses based on symptoms. The doctor can use this table to facilitate further questioning and testing of the patient in order to decide on the final diagnosis.

Result
+--------------------------------------------------+
| n.name | d.name | score |
+--------------------------------------------------+
| "Sally Anderson" | "Acute Placeboitis" | 4 |
| "Sally Anderson" | "Chronic Argitis" | 2 |
| "Sally Anderson" | "Acute Argitis" | 2 |
| "Sally Anderson" | "Acute Otheritis" | 2 |
| "Sally Anderson" | "Chronic Placeboitis" | 2 |
| "Sally Anderson" | "Acute Yellowitis" | 2 |
| "Sally Anderson" | "Chronic Whatitis" | 2 |
| "Sally Anderson" | "Chronic Yellowitis" | 2 |
| "Sally Anderson" | "Acute Whatitis" | 1 |
| "Sally Anderson" | "Acute Someitis" | 1 |
| "Sally Anderson" | "Chronic Someitis" | 1 |
| "Sally Anderson" | "Chronic Otheritis" | 1 |
+--------------------------------------------------+
12 rows
ready to start consuming query after 48 ms, results consumed after another 2 ms

Once the doctor has investigated further, they would be able to decide on the diagnosis and save that result to the database:

WITH datetime({epochmillis:timestamp()}) AS now
WITH now, date(now) as today
MATCH (p:Patient)
 WHERE p.ssn = 1234657
MATCH (d:Disease)
 WHERE d.name = "Chronic Placeboitis"
MERGE (p)-[i:DIAGNOSIS {by: 'Alice'}]->(d)
 ON CREATE SET i.created_at = now, i.updated_at = now, i.date = today
 ON MATCH SET i.updated_at = now
RETURN p.name, d.name, i.by, i.date, duration.between(i.created_at, i.updated_at) AS updated;

This allows the doctor to record their diagnosis as well as take note of previous diagnoses:

Result
+---------------------------------------------------------------------------+
| p.name | d.name | i.by | i.date | updated |
+---------------------------------------------------------------------------+
| "Sally Anderson" | "Chronic Placeboitis" | "Alice" | 2025年02月14日 | PT0S |
+---------------------------------------------------------------------------+
1 row
ready to start consuming query after 73 ms, results consumed after another 6 ms
Created 1 relationships, Set 4 properties

Creating the DIAGNOSIS relationship for the first time requires the privilege to create new types. This is also true for the property names doctor, created_at, and updated_at. It can be fixed by either granting the doctor NAME MANAGEMENT privileges or by pre-creating the missing types. The latter would be more precise and can be achieved by running, as an administrator, the procedures db.createRelationshipType and db.createProperty with appropriate arguments.

Privileges of receptionist

Receptionists should only be able to manage patient information. They are not allowed to find or read any other parts of the graph. In addition, they should be able to create and delete patients, but not any other nodes. Switch to the system database and run the following commands:

CREATE ROLE receptionist;
GRANT MATCH {*} ON GRAPH healthcare NODES Patient TO receptionist;
GRANT CREATE ON GRAPH healthcare NODES Patient TO receptionist;
GRANT DELETE ON GRAPH healthcare NODES Patient TO receptionist;
GRANT SET PROPERTY {*} ON GRAPH healthcare NODES Patient TO receptionist;

It would have been simpler to grant global WRITE privileges to the receptionist Bob. However, this would have the unfortunate side effect of allowing them the ability to create other nodes, like new Symptom nodes, even though they would subsequently be unable to find or read those same nodes. While there are use cases in which it is desirable to have roles able to create data they cannot read, that is not the case of this model.

With that in mind, grant the receptionist Bob their new receptionist role:

GRANT ROLE receptionist TO bob;

With these privileges, if Bob tries to read the entire database, he will still only see the patients:

MATCH (n) WITH labels(n) AS labels
RETURN labels, count(*);
Result
+------------------------+
| labels | count(*) |
+------------------------+
| ["Patient"] | 101 |
+------------------------+
1 row
ready to start consuming query after 2 ms, results consumed after another 3 ms

However, Bob is able to see all fields of the patients' records:

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
Result
+-----------------------------------------------------------------------+
| n.name | n.ssn | n.address | n.dateOfBirth |
+-----------------------------------------------------------------------+
| "Mark Jackson" | 1234578 | "1 secret way, downtown" | 1970年11月29日 |
| "Joe Svensson" | 1234579 | "1 secret way, downtown" | 1972年02月12日 |
| "Bob Anderson" | 1234597 | "1 secret way, downtown" | 1970年04月27日 |
| "Sally Anderson" | 1234617 | "1 secret way, downtown" | 1970年12月02日 |
| "Ally Anderson" | 1234622 | "1 secret way, downtown" | 1972年05月20日 |
| "Jane Svensson" | 1234644 | "1 secret way, downtown" | 1970年05月24日 |
| "Sally Anderson" | 1234657 | "1 secret way, downtown" | 1972年01月23日 |
+-----------------------------------------------------------------------+
7 rows
ready to start consuming query after 2 ms, results consumed after another 1 ms

With the receptionist role, Bob can delete any new patient nodes they have just created, but they are not able to delete patients that have already received diagnoses since those are connected to parts of the graph that Bob cannot see. Here is a demonstration of both scenarios:

CREATE (n:Patient {
 ssn:87654321,
 name: 'Another Patient',
 email: 'another@example.com',
 address: '1 secret way, downtown',
 dateOfBirth: date('2001-01-20')
})
RETURN n.name, n.dateOfBirth;
Result
+-----------------------------------+
| n.name | n.dateOfBirth |
+-----------------------------------+
| "Another Patient" | 2001年01月20日 |
+-----------------------------------+
1 row
ready to start consuming query after 36 ms, results consumed after another 1 ms
Added 1 nodes, Set 5 properties, Added 1 labels

The receptionist is able to modify any patient record:

MATCH (n:Patient)
WHERE n.ssn = 87654321
SET n.address = '2 streets down, uptown'
RETURN n.name, n.dateOfBirth, n.address;
Result
+--------------------------------------------------------------+
| n.name | n.dateOfBirth | n.address |
+--------------------------------------------------------------+
| "Another Patient" | 2001年01月20日 | "2 streets down, uptown" |
+--------------------------------------------------------------+
1 row
ready to start consuming query after 22 ms, results consumed after another 3 ms
Set 1 properties

The receptionist is also able to delete this recently created patient because it is not connected to any other records:

MATCH (n:Patient)
 WHERE n.ssn = 87654321
DETACH DELETE n;
Result
0 rows
ready to start consuming query after 17 ms, results consumed after another 0 ms
Deleted 1 nodes

However, if the receptionist attempts to delete a patient that has existing diagnoses, this will fail:

MATCH (n:Patient)
 WHERE n.ssn = 1234610
DETACH DELETE n;
Result
Cannot delete node<65>, because it still has relationships. To delete this node, you must first delete its relationships.

The reason why this query fails is that, while Bob can find the (:Patient) node, he does not have sufficient traverse rights to find nor the rights to delete the outgoing relationships from it.

Either they need to ask Tina the itadmin for help for this task, or more privileges can be added to the receptionist role. Switch to the system database and run the following commands:

GRANT TRAVERSE ON GRAPH healthcare NODES Symptom, Disease TO receptionist;
GRANT TRAVERSE ON GRAPH healthcare RELATIONSHIPS HAS, DIAGNOSIS TO receptionist;
GRANT DELETE ON GRAPH healthcare RELATIONSHIPS HAS, DIAGNOSIS TO receptionist;

Privileges that were granted or denied earlier can be revoked using the REVOKE command.

Privileges of nurse

Nurses should have the capabilities of both doctors and receptionists, but assigning them both the doctor and receptionist roles might not have the expected effect. If those two roles were created with GRANT privileges only, combining them would be simply cumulative. But if the doctor role contains some DENY privileges, these always overrule GRANT. This means that the nurse will still have the same restrictions as a doctor, which is not what is intended here.

To demonstrate this, you can assign the doctor role to the nurse Daniel. Switch to the system database and run the following commands:

GRANT ROLE doctor, receptionist TO daniel;

Daniel should now have a combined set of privileges:

SHOW USER daniel PRIVILEGES AS COMMANDS;
Result
+---------------------------------------------------------------------------+
| command |
+---------------------------------------------------------------------------+
| "DENY READ {address} ON GRAPH `healthcare` NODE Patient TO $role" |
| "DENY SET PROPERTY {address} ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT ACCESS ON DATABASE `healthcare` TO $role" |
| "GRANT ACCESS ON HOME DATABASE TO $role" |
| "GRANT CREATE ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT DELETE ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT DELETE ON GRAPH `healthcare` RELATIONSHIP DIAGNOSIS TO $role" |
| "GRANT DELETE ON GRAPH `healthcare` RELATIONSHIP HAS TO $role" |
| "GRANT EXECUTE FUNCTION * ON DBMS TO $role" |
| "GRANT EXECUTE PROCEDURE * ON DBMS TO $role" |
| "GRANT LOAD ON ALL DATA TO $role" |
| "GRANT MATCH {*} ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT READ {*} ON GRAPH `healthcare` NODE * TO $role" |
| "GRANT READ {*} ON GRAPH `healthcare` RELATIONSHIP * TO $role" |
| "GRANT SET PROPERTY {*} ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` NODE * TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` NODE Disease TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` NODE Symptom TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` RELATIONSHIP * TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` RELATIONSHIP DIAGNOSIS TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` RELATIONSHIP HAS TO $role" |
| "GRANT WRITE ON GRAPH `healthcare` TO $role" |
+---------------------------------------------------------------------------+
22 rows
ready to start consuming query after 10 ms, results consumed after another 1 ms

Privileges that were granted or denied earlier can be revoked using the REVOKE command.

Now the intention is that a nurse can perform the actions of a receptionist, which means they should be able to read and write the address field of the Patient nodes. Log in as daniel and run the following query against the healthcare database:

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
Result
+--------------------------------------------------------+
| n.name | n.ssn | n.address | n.dateOfBirth |
+--------------------------------------------------------+
| "Mark Jackson" | 1234578 | NULL | 1970年11月29日 |
| "Joe Svensson" | 1234579 | NULL | 1972年02月12日 |
| "Bob Anderson" | 1234597 | NULL | 1970年04月27日 |
| "Sally Anderson" | 1234617 | NULL | 1970年12月02日 |
| "Ally Anderson" | 1234622 | NULL | 1972年05月20日 |
| "Jane Svensson" | 1234644 | NULL | 1970年05月24日 |
| "Sally Anderson" | 1234657 | NULL | 1972年01月23日 |
+--------------------------------------------------------+
7 rows
ready to start consuming query after 4 ms, results consumed after another 2 ms

As expected, the address field is invisible to the nurse. This happens because, as previously described, DENY privileges always overrule GRANT. Since both roles doctor and receptionist were assigned to the nurse, the DENIED privileges of the doctor role are overruling the GRANTED privileges of the receptionist. Even if the nurse tries to write the address field, they would receive an error, and that is not what is desired here. To correct that, you can:

  • Redefine the doctor role with only grants and define each Patient property the doctor should be able to read.

  • Redefine the nurse role with the actual intended behavior.

The second option is simpler if you consider that the nurse is essentially the doctor without the address restrictions. In this case, you need to create a nurse role from scratch. Switch to the system database and run the following commands:

CREATE ROLE nurse;
GRANT TRAVERSE ON GRAPH healthcare TO nurse;
GRANT READ {*} ON GRAPH healthcare TO nurse;
GRANT WRITE ON GRAPH healthcare TO nurse;

Now you assign the nurse role to the nurse Daniel, but remember to revoke the doctor and the receptionist roles so there are no privileges being overridden:

REVOKE ROLE doctor FROM daniel;
REVOKE ROLE receptionist FROM daniel;
GRANT ROLE nurse TO daniel;

This time, when the nurse Daniel logs in and runs the following query against the healthcare database, they will see the address fields:

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
Result
+-----------------------------------------------------------------------+
| n.name | n.ssn | n.address | n.dateOfBirth |
+-----------------------------------------------------------------------+
| "Mark Jackson" | 1234578 | "1 secret way, downtown" | 1970年11月29日 |
| "Joe Svensson" | 1234579 | "1 secret way, downtown" | 1972年02月12日 |
| "Bob Anderson" | 1234597 | "1 secret way, downtown" | 1970年04月27日 |
| "Sally Anderson" | 1234617 | "1 secret way, downtown" | 1970年12月02日 |
| "Ally Anderson" | 1234622 | "1 secret way, downtown" | 1972年05月20日 |
| "Jane Svensson" | 1234644 | "1 secret way, downtown" | 1970年05月24日 |
| "Sally Anderson" | 1234657 | "1 secret way, downtown" | 1972年01月23日 |
+-----------------------------------------------------------------------+
7 rows
ready to start consuming query after 4 ms, results consumed after another 2 ms

The other main action that the nurse role should be able to perform is the primary doctor action of saving a diagnosis to the database:

WITH date(datetime({epochmillis:timestamp()})) AS today
MATCH (p:Patient)
 WHERE p.ssn = 1234657
MATCH (d:Disease)
 WHERE d.name = "Chronic Placeboitis"
MERGE (p)-[i:DIAGNOSIS {by: 'Daniel'}]->(d)
 ON CREATE SET i.date = today
RETURN p.name, d.name, i.by, i.date;
Result
+------------------------------------------------------------------+
| p.name | d.name | i.by | i.date |
+------------------------------------------------------------------+
| "Sally Anderson" | "Chronic Placeboitis" | "Daniel" | 2025年02月14日 |
+------------------------------------------------------------------+
1 row
ready to start consuming query after 49 ms, results consumed after another 2 ms
Created 1 relationships, Set 2 properties

Performing this action, otherwise reserved for the doctor role, involves more responsibility for the nurse. There might be nurses that should not be entrusted with this option, which is why you can divide the nurse role into senior and junior nurses, for example. Currently, Daniel is a senior nurse.

Privileges of junior nurse

Previously, creating the nurse role by combining the doctor and receptionist roles led to an undesired scenario as the DENIED privileges of the doctor role overrode the GRANTED privileges of the receptionist. In that case, the objective was to enhance the permissions of the senior nurse, but when it comes to the junior nurse, they should be able to perform the same actions as the senior, except adding diagnoses to the database.

To achieve this, you can create a special role that contains specifically only the additional restrictions. Switch to the system database and run the following commands:

CREATE ROLE disableDiagnoses;
DENY CREATE ON GRAPH healthcare RELATIONSHIPS DIAGNOSIS TO disableDiagnoses;

And then assign this new role to the nurse Daniel, so you can test the behavior:

GRANT ROLE disableDiagnoses TO daniel;

If you check what privileges Daniel has now, it is the combination of the two roles nurse and disableDiagnoses:

SHOW USER daniel PRIVILEGES AS COMMANDS;
Result
+---------------------------------------------------------------------+
| command |
+---------------------------------------------------------------------+
| "DENY CREATE ON GRAPH `healthcare` RELATIONSHIP DIAGNOSIS TO $role" |
| "GRANT ACCESS ON DATABASE `healthcare` TO $role" |
| "GRANT ACCESS ON HOME DATABASE TO $role" |
| "GRANT EXECUTE FUNCTION * ON DBMS TO $role" |
| "GRANT EXECUTE PROCEDURE * ON DBMS TO $role" |
| "GRANT LOAD ON ALL DATA TO $role" |
| "GRANT READ {*} ON GRAPH `healthcare` NODE * TO $role" |
| "GRANT READ {*} ON GRAPH `healthcare` RELATIONSHIP * TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` NODE * TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` RELATIONSHIP * TO $role" |
| "GRANT WRITE ON GRAPH `healthcare` TO $role" |
+---------------------------------------------------------------------+
11 rows
ready to start consuming query after 4 ms, results consumed after another 1 ms

Daniel can still see the address fields, and can even perform the diagnosis investigation that the doctor can perform:

MATCH (n:Patient)-[:HAS]->(s:Symptom)-[:OF]->(d:Disease)
WHERE n.ssn = 1234650
RETURN n.ssn, n.name, d.name, count(s) AS score ORDER BY score DESC;
Result
+--------------------------------------------------------+
| n.ssn | n.name | d.name | score |
+--------------------------------------------------------+
| 1234650 | "Mark Smith" | "Chronic Argitis" | 3 |
| 1234650 | "Mark Smith" | "Chronic Otheritis" | 3 |
| 1234650 | "Mark Smith" | "Acute Placeboitis" | 3 |
| 1234650 | "Mark Smith" | "Chronic Yellowitis" | 3 |
| 1234650 | "Mark Smith" | "Chronic Someitis" | 3 |
| 1234650 | "Mark Smith" | "Chronic Whatitis" | 2 |
| 1234650 | "Mark Smith" | "Acute Otheritis" | 2 |
| 1234650 | "Mark Smith" | "Chronic Placeboitis" | 2 |
| 1234650 | "Mark Smith" | "Acute Argitis" | 2 |
| 1234650 | "Mark Smith" | "Acute Someitis" | 2 |
| 1234650 | "Mark Smith" | "Acute Whatitis" | 1 |
| 1234650 | "Mark Smith" | "Acute Yellowitis" | 1 |
+--------------------------------------------------------+
12 rows
ready to start consuming query after 50 ms, results consumed after another 1 ms

But when they try to save a diagnosis to the database, they will be denied that action:

WITH date(datetime({epochmillis:timestamp()})) AS today
MATCH (p:Patient)
 WHERE p.ssn = 1234650
MATCH (d:Disease)
 WHERE d.name = "Chronic Placeboitis"
MERGE (p)-[i:DIAGNOSIS {by: 'Daniel'}]->(d)
 ON CREATE SET i.date = today
RETURN p.name, d.name, i.by, i.date;
Result
+---------------------------------+
| p.name | d.name | i.by | i.date |
+---------------------------------+
Create relationship with type 'DIAGNOSIS' on database 'healthcare' is not allowed for user 'daniel' with roles [PUBLIC, disableDiagnoses, nurse].

To promote Daniel back to senior nurse, revoke the role that introduced the restriction:

REVOKE ROLE disableDiagnoses FROM daniel;

Building a custom administrator role

The itadmin role was originally created by copying the built-in admin role and adding restrictions. However, there might be cases in which having `DENY`s can be less convenient than only having `GRANT`s. Instead, you can build the administrator role from the ground up.

The IT administrator Tina is able to create new users and assign them to the product roles as an itadmin, but you can create a more restricted role called userManager and grant it only the appropriate privileges. Switch to the system database and run the following commands:

CREATE ROLE userManager;
GRANT USER MANAGEMENT ON DBMS TO userManager;
GRANT ROLE MANAGEMENT ON DBMS TO userManager;
GRANT SHOW PRIVILEGE ON DBMS TO userManager;

Test the new behavior by revoking the itadmin role from Tina and grant them the userManager role instead:

REVOKE ROLE itadmin FROM tina;
GRANT ROLE userManager TO tina;

These are the privileges granted to userManager:

  • USER MANAGEMENT allows creating, updating, and dropping users.

  • ROLE MANAGEMENT allows creating, updating, and dropping roles as well as assigning roles to users.

  • SHOW PRIVILEGE allows listing the users' privileges.

Listing Tina’s new privileges should now show a much shorter list than when they were a more powerful administrator with the itadmin role:

SHOW USER tina PRIVILEGES AS COMMANDS;
Result
+--------------------------------------------------+
| command |
+--------------------------------------------------+
| "GRANT ACCESS ON DATABASE `healthcare` TO $role" |
| "GRANT ACCESS ON HOME DATABASE TO $role" |
| "GRANT EXECUTE FUNCTION * ON DBMS TO $role" |
| "GRANT EXECUTE PROCEDURE * ON DBMS TO $role" |
| "GRANT LOAD ON ALL DATA TO $role" |
| "GRANT ROLE MANAGEMENT ON DBMS TO $role" |
| "GRANT SHOW PRIVILEGE ON DBMS TO $role" |
| "GRANT USER MANAGEMENT ON DBMS TO $role" |
+--------------------------------------------------+
8 rows
ready to start consuming query after 24 ms, results consumed after another 1 ms

No other privilege management privileges were granted here. How much power this role should have would depend on the requirements of the system. Refer to the section The admin role for a complete list of privileges to consider.

Now Tina should be able to create new users and assign them to roles (from the system database):

CREATE USER sally SET PASSWORD 'secretpassword' CHANGE REQUIRED;
GRANT ROLE receptionist TO sally;
SHOW USER sally PRIVILEGES AS COMMANDS;
Result
0 rows
ready to start consuming query after 45 ms, results consumed after another 0 ms
0 rows
ready to start consuming query after 4 ms, results consumed after another 0 ms
+------------------------------------------------------------------------+
| command |
+------------------------------------------------------------------------+
| "GRANT ACCESS ON DATABASE `healthcare` TO $role" |
| "GRANT ACCESS ON HOME DATABASE TO $role" |
| "GRANT CREATE ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT DELETE ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT DELETE ON GRAPH `healthcare` RELATIONSHIP DIAGNOSIS TO $role" |
| "GRANT DELETE ON GRAPH `healthcare` RELATIONSHIP HAS TO $role" |
| "GRANT EXECUTE FUNCTION * ON DBMS TO $role" |
| "GRANT EXECUTE PROCEDURE * ON DBMS TO $role" |
| "GRANT LOAD ON ALL DATA TO $role" |
| "GRANT MATCH {*} ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT SET PROPERTY {*} ON GRAPH `healthcare` NODE Patient TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` NODE Disease TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` NODE Symptom TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` RELATIONSHIP DIAGNOSIS TO $role" |
| "GRANT TRAVERSE ON GRAPH `healthcare` RELATIONSHIP HAS TO $role" |
+------------------------------------------------------------------------+
15 rows
ready to start consuming query after 3 ms, results consumed after another 0 ms

AltStyle によって変換されたページ (->オリジナル) /