细粒度访问控制

在创建数据库时,管理员可能需要确定哪些用户可以访问特定的信息。

正如 内置角色和权限 所述,Neo4j 已经提供了针对特定权限(即读取、编辑或写入)预配置的预设角色。虽然这些内置角色涵盖了许多常见的日常场景,但也可以根据特定需求创建自定义角色。

本教程将引导您完成一个医疗用例,该用例阐述了安全性和细粒度访问控制的各个方面。

医疗用例

为了演示这些工具的应用,请考虑一个医疗数据库的示例,它可能适用于医疗诊所或医院。

为简化起见,仅使用三个标签来表示以下实体

(:Patient)

因出现某些症状而就诊的患者。患者的具体信息可以通过属性捕获

  • 名称 (name)

  • ssn(社会安全号码)

  • address

  • dateOfBirth(出生日期)

(:Symptom)

已知疾病目录中发现的一组症状。可以使用属性对它们进行描述

  • 名称 (name)

  • description(描述)

(:Disease)

数据库中已知疾病的映射目录。可以使用属性对它们进行描述

  • 名称 (name)

  • description(描述)

这些实体被建模为节点,并通过以下类型的关系连接

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

当患者到诊所就诊时,他们会向护士或医生描述自己的症状。护士或医生随后将此信息以患者节点与已知症状图之间的连接形式输入数据库。此关系中可能感兴趣的属性包括

  • date - 报告症状的日期。

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

症状是已知疾病图中的一个子图。症状与疾病之间的关系可以包含一个概率因子,表示患有该疾病的人表现出该症状的可能性或常见程度。这将使医生能够更轻松地使用统计查询进行诊断。

  • probability - 症状与疾病匹配的概率。

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

医生可以使用疾病及其症状图对最可能匹配患者的疾病进行初步调查。在此基础上,结合他们对患者的评估,医生可能会做出诊断,并通过添加具有适当属性的此关系将其持久化到图中

  • by:医生姓名

  • date:诊断日期

  • description:医生补充备注

security example
图 1. 医疗用例

同一个数据库将被许多不同的用户使用,每个用户都有不同的访问需求

  • 医生:需要诊断患者。

  • 护士:需要治疗患者。

  • 接待员:需要识别和记录患者信息。

  • 研究人员:需要对医疗数据进行统计分析。

  • IT 管理员:需要管理数据库,例如创建和分配用户。

要创建数据库,请首先创建表示上述实体的节点和关系。

创建 healthcare 数据库

以下步骤假设您已安装 Neo4j 企业版且其正在运行。有关详细信息,请参阅 安装

  1. 使用 Cypher Shell,以 neo4j 用户身份登录 system 数据库

    cypher-shell -u neo4j -p my-password -d system
  2. 创建 healthcare 数据库

    CREATE DATABASE healthcare;
  3. healthcare 数据库设置为默认数据库

    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

将数据导入 healthcare 数据库

创建表示上述 医疗用例 一节中描述的实体的节点和关系。

  1. 切换到 healthcare 数据库

    :use healthcare;
  2. 创建一些症状数据

    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;
    结果
    +-------------------------------+
    | 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. 创建一些疾病数据

    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;
    结果
    +-----------------------------------------------+
    | 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. 创建症状与疾病之间的关系

    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;
    结果
    +---------------------------------------------------------+
    | 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. 确保可以使用尚未存在的类型

    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');
    结果
    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. 创建一些患者数据

    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);
    结果
    +----------+
    | 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. 创建患者与症状之间的关系

    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;
    结果
    +------------------------------------------------------------+
    | 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

管理授权和访问控制

与那些通常要求在应用程序内部建模用户的应用程序不同,数据库提供诸如角色和权限之类的用户管理资源。这允许用户完全在数据库安全模型内创建,这种策略允许将对数据的访问与数据本身分离开来。有关更多信息,请参阅 身份验证和授权

在本教程中,请考虑 healthcare 数据库的五个用户

  • Alice,医生。

  • Daniel,护士。

  • Bob,接待员。

  • Charlie,研究人员。

  • Tina,IT 管理员。

您可以使用 CREATE USER 命令(从 system 数据库)创建这些用户

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;

此时,用户无法与数据库交互,因此需要通过使用角色来授予这些能力。有两种不同的方式来实现这一点:使用 内置角色和权限,或者使用具有 子图访问控制 细粒度权限的高级资源。

使用内置角色进行访问控制

Neo4j 附带了内置角色,涵盖了许多常见需求

  • PUBLIC - 所有用户都拥有此角色。默认情况下,他们可以访问主数据库、加载数据以及运行所有过程和用户定义函数。

  • reader - 可以读取所有数据库中的数据。

  • editor - 可以读取和更新所有数据库,但不能通过新的标签、关系类型或属性名称扩展模式。

  • publisher - 可以读取和编辑,以及添加新的标签、关系类型和属性名称。

  • architect - 拥有 publisher 的所有能力,以及管理索引和约束的能力。

  • admin - 可以执行 architect 操作,以及加载数据和管理数据库、用户、角色和权限。

考虑用户示例中的 Charlie。作为一名研究人员,他们不需要对数据库的写入访问权限,因此被分配了 reader 角色。

另一方面,Alice(医生)、Daniel(护士)和 Bob(接待员)都需要用新的患者信息更新数据库,但不需要通过新的标签、关系类型、属性名称或索引来扩展模式。因此,他们都被分配了 editor 角色。

安装和管理数据库的 IT 管理员 Tina 需要被分配 admin 角色。

以下是如何向用户授予角色(从 system 数据库)

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;

使用权限进行子图访问控制

上述方法的一个局限性是它允许所有用户查看数据库上的所有数据。然而在许多现实场景中,最好建立一些访问限制。

例如,您可能希望限制研究人员对患者个人信息的访问,或者限制接待员在数据库上写入新标签。虽然这些限制可以在应用层进行编码,但通过创建自定义角色并为其分配特定权限,直接在 Neo4j 安全模型内强制执行细粒度限制是可行且更安全的。

由于将创建新的自定义角色,因此首先撤销分配给现有用户的角色非常重要。针对 system 数据库运行以下命令

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;

现在您可以基于 权限(privileges)的概念创建自定义角色,这允许更好地控制每个用户能够做什么。为了正确分配这些权限,首先确定每种类型的用户

医生

应该能够读取和写入大部分图,但被禁止读取患者的地址。有权将 诊断 保存到数据库,但不能通过新概念扩展模式。

接待员

应该能够读取和写入所有患者数据,但不能查看症状、疾病或诊断。

研究人员

应该能够对所有数据执行统计分析,除了患者个人信息(对此他们应受到访问限制)。为了说明设置相同有效权限的两种不同方式,创建了两个角色以供比较。

护士

应该能够执行医生和接待员都能执行的所有任务。向护士授予这两个角色(医生和接待员)并不能按预期工作。这将在致力于创建 nurse 角色的部分中解释。

初级护士

虽然高级护士能够像医生一样保存诊断,但某些(初级)护士可能不被允许这样做。从头创建一个新角色是一种选择,但可以通过将 nurse 角色与专门限制该活动的新的 disableDiagnoses 角色结合起来实现相同的效果。

IT 管理员

此角色与内置的 admin 角色非常相似,区别在于它不应允许访问患者的 SSN,也不能保存诊断(这是仅限医疗专业人员的特权)。为了实现这一点,可以复制并相应地修改内置的 admin 角色。

用户管理器

该用户应具有与 IT 管理员类似的访问权限,但有更多限制。为了实现这一点,可以从头创建一个新角色,并仅为其分配特定的管理能力。

在创建新角色并将它们分配给 Alice、Bob、Daniel、Charlie 和 Tina 之前,定义每个角色应具备的权限非常重要。由于所有用户都需要对 healthcare 数据库拥有 ACCESS 权限,因此可以通过 PUBLIC 角色而不是所有单独的角色来设置

针对 system 数据库运行以下命令

GRANT ACCESS ON DATABASE healthcare TO PUBLIC;

itadmin 的权限

可以创建一个作为内置 admin 角色副本的角色

CREATE ROLE itadmin AS COPY OF admin;

然后你需要 拒绝 (deny) 该角色不应执行的两个特定操作

  • 读取任何患者的社会安全号码 (SSN)。

  • 提交医疗诊断。

以及 itadmin 修改其自身权限的能力。

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;

可以使用以下命令查看分配给 itadmin 角色的用户可用的整套权限

SHOW ROLE itadmin PRIVILEGES AS COMMANDS;
结果
+-------------------------------------------------------------------------+
| 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

早些时候授予或拒绝的权限可以使用 REVOKE 命令 进行撤销。

为了向 IT 管理员 tina 提供这些权限,必须为他们分配新角色 itadmin。针对 system 数据库运行以下命令

GRANT ROLE itadmin TO tina;

为了演示 Tina 无法查看患者的 SSN,您可以以 tina 身份登录 Cypher Shell 并针对 healthcare 数据库运行以下查询

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
结果
+---------------------------------------------------------------------+
| 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

结果看起来好像这些节点甚至没有 SSN 字段。这是安全模型的一个关键特性:用户无法区分不存在的数据和使用细粒度读取权限隐藏的数据。

现在回想一下 itadmin 角色被拒绝了保存诊断的能力(因为这是一项仅保留给医生和高级医务人员的关键医疗功能),您可以通过尝试创建 DIAGNOSIS 关系来测试这一点

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

读取数据的限制不会导致错误,它们只会让数据看起来不存在。但是,当用户尝试执行不允许的操作时,更新图的限制确实会输出适当的错误。

researcher 的权限

研究人员 Charlie 之前是一个只读用户。要为他们分配所需的权限,您可以做类似于 itadmin 角色所做的事情,这次是复制和修改 reader 角色。

另一种方法是从头创建一个新角色,然后授予或拒绝一系列权限

  • 拒绝权限:

    您可以授予 researcher 角色查找所有节点和读取所有属性的能力(非常类似于 reader 角色),但拒绝读取 Patient 属性的权限。这样,研究人员将无法查看患者信息,例如 nameSSNaddress。不过,这种方法有一个问题:如果在将限制分配给 researcher 角色之后Patient 节点添加了更多属性,这些新属性将自动对研究人员可见——这可能是不希望看到的结果。

    为了避免这种情况,您可以针对 system 数据库运行以下命令来拒绝特定权限。您必须以拥有 admin 角色的用户身份登录,才能执行这些命令

    // 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;
  • 授予权限:

    另一种选择是仅提供对研究人员被允许查看的属性的特定访问权限。这样,添加新属性(例如,添加到 Patient 节点)不会自动使分配了此角色的用户可见。如果您希望使它们可见,则需要显式授予读取权限。针对 system 数据库运行以下命令。您必须以拥有 admin 角色的用户身份登录,才能执行这些命令

    // 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;

    为了测试研究人员 Charlie 现在是否拥有指定的权限,请为他们分配 researcherB 角色(具有明确拒绝的权限)

    GRANT ROLE researcherB TO charlie;

    您还可以使用 SHOW PRIVILEGES 命令的一个版本来查看 Charlie 的访问权限,这是分配给 researcherBPUBLIC 角色的权限的组合

    SHOW USER charlie PRIVILEGES AS COMMANDS;
    结果
    +-----------------------------------------------------------------------+
    | 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

    现在,当 Charlie 登录到 Cypher Shell 并尝试针对 healthcare 数据库执行以下命令时,即使该命令与 itadmin 之前使用的命令类似,他们也会看到不同的结果

    MATCH (n:Patient)
     WHERE n.dateOfBirth < date('1972-06-12')
    RETURN n.name, n.ssn, n.address, n.dateOfBirth;
    结果
    +--------------------------------------------+
    | 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

    只有出生日期可用,以便研究人员 Charlie 可以执行统计分析。Charlie 可以尝试的另一个查询是查找 25 岁以下患者最可能被诊断出的十种疾病,按概率列出

    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;
    结果
    +-------------------------------------------+
    | 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

    如果 Charlie 的 researcherB 角色被撤销,但授予了 researcherW 角色,在重新运行这些查询时,将获得相同的结果。

    早些时候授予或拒绝的权限可以使用 REVOKE 命令 进行撤销。

doctor 的权限

医生应该被赋予读取和写入几乎所有内容的能力,例如,除了患者的 address 属性。可以通过分配完全读取和写入访问权限,然后专门拒绝访问 address 属性,从头开始构建此角色。切换到 system 数据库并运行以下命令

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;

为了让医生 Alice 拥有这些权限,授予用户 alice 这个新角色

GRANT ROLE doctor TO alice;

为了演示 Alice 无法查看患者地址,请以 alice 身份登录并针对 healthcare 数据库运行以下查询

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
结果
+--------------------------------------------------------+
| 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

结果是,医生拥有预期的权限,包括能够查看患者的 SSN,但不能查看他们的地址。

医生也能够查看所有其他节点类型

MATCH (n) WITH labels(n) AS labels
RETURN labels, count(*);
结果
+------------------------+
| labels      | count(*) |
+------------------------+
| ["Symptom"] | 10       |
| ["Disease"] | 12       |
| ["Patient"] | 101      |
+------------------------+

3 rows
ready to start consuming query after 29 ms, results consumed after another 1 ms

此外,医生可以遍历图,找到与患者连接的症状和疾病

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;

结果表显示了基于症状最可能的诊断。医生可以使用此表来促进对患者的进一步询问和测试,以便做出最终诊断。

结果
+--------------------------------------------------+
| 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

一旦医生进行了进一步调查,他们就能够确定诊断并将该结果保存到数据库中

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;

这允许医生记录他们的诊断以及记录以前的诊断

结果
+---------------------------------------------------------------------------+
| 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

首次创建 DIAGNOSIS 关系需要创建新类型的权限。这对于属性名称 doctorcreated_atupdated_at 也适用。可以通过授予医生 NAME MANAGEMENT 权限或预先创建缺失的类型来解决此问题。后者更精确,并且可以作为管理员运行带有适当参数的过程 db.createRelationshipTypedb.createProperty 来实现。

receptionist 的权限

接待员应该只能管理患者信息。他们不允许查找或读取图的任何其他部分。此外,他们应该能够创建和删除患者,但不能创建和删除任何其他节点。切换到 system 数据库并运行以下命令

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;

授予接待员 Bob 全局 WRITE 权限会更简单。但是,这会产生一个不幸的副作用,即允许他们创建其他节点(如新的 Symptom 节点),即使他们随后无法查找或读取这些节点。虽然在某些用例中,让角色能够创建他们无法读取的数据是可取的,但在此模型中并非如此。

考虑到这一点,授予接待员 Bob 他们的新 receptionist 角色

GRANT ROLE receptionist TO bob;

有了这些权限,如果 Bob 尝试读取整个数据库,他仍然只会看到患者

MATCH (n) WITH labels(n) AS labels
RETURN labels, count(*);
结果
+------------------------+
| labels      | count(*) |
+------------------------+
| ["Patient"] | 101      |
+------------------------+

1 row
ready to start consuming query after 2 ms, results consumed after another 3 ms

但是,Bob 能够查看患者记录的所有字段

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
结果
+-----------------------------------------------------------------------+
| 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

拥有 receptionist 角色,Bob 可以删除他们刚刚创建的任何新患者节点,但他们无法删除已经收到诊断的患者,因为那些患者连接到了 Bob 无法查看的图的部分。以下是两种情况的演示

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;
结果
+-----------------------------------+
| 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

接待员能够修改任何患者记录

MATCH (n:Patient)
WHERE n.ssn = 87654321
SET n.address = '2 streets down, uptown'
RETURN n.name, n.dateOfBirth, n.address;
结果
+--------------------------------------------------------------+
| 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

接待员也能够删除这个最近创建的患者,因为它没有连接到任何其他记录

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

但是,如果接待员尝试删除具有现有诊断的患者,这将失败

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

此查询失败的原因是,虽然 Bob 可以找到 (:Patient) 节点,但他没有足够的遍历权限来查找它,也没有权限删除来自它的传出关系。

要么他们需要请求 IT 管理员 itadmin Tina 协助完成此任务,要么可以向 receptionist 角色添加更多权限。切换到 system 数据库并运行以下命令

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;

早些时候授予或拒绝的权限可以使用 REVOKE 命令 进行撤销。

nurse 的权限

护士应该拥有医生和接待员的能力,但同时分配给他们 doctorreceptionist 角色可能不会产生预期的效果。如果这两个角色仅使用 GRANT 权限创建,则将它们组合起来将只是累加。但如果 doctor 角色包含一些 DENY 权限,这些权限总是覆盖 GRANT。这意味着护士仍然会拥有与医生相同的限制,这并非此处所意图的。

为了演示这一点,您可以将 doctor 角色分配给护士 Daniel。切换到 system 数据库并运行以下命令

GRANT ROLE doctor, receptionist TO daniel;

Daniel 现在应该拥有一组组合的权限

SHOW USER daniel PRIVILEGES AS COMMANDS;
结果
+---------------------------------------------------------------------------+
| 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

早些时候授予或拒绝的权限可以使用 REVOKE 命令 进行撤销。

现在意图是护士可以执行接待员的操作,这意味着他们应该能够读取和写入 Patient 节点的 address 字段。以 daniel 身份登录并针对 healthcare 数据库运行以下查询

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
结果
+--------------------------------------------------------+
| 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

正如预期的那样,address 字段对护士不可见。发生这种情况是因为,如前所述,DENY 权限总是覆盖 GRANT。由于 doctorreceptionist 两个角色都被分配给了护士,doctor 角色的 DENIED 权限正在覆盖 receptionist 角色的 GRANTED 权限。即使护士尝试写入地址字段,他们也会收到错误,而这并非此处所期望的。为了纠正这一点,您可以

  • 重新定义 doctor 角色,仅使用授予 (grants),并定义医生应该能够读取的每个 Patient 属性。

  • 重新定义 nurse 角色,使其具有实际预期的行为。

如果您认为护士本质上是去掉了 address 限制的医生,那么第二种选择更简单。在这种情况下,您需要从头开始创建一个 nurse 角色。切换到 system 数据库并运行以下命令

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

现在将 nurse 角色分配给护士 Daniel,但记得撤销 doctorreceptionist 角色,这样就不会有权限被覆盖

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

这一次,当护士 Daniel 登录并针对 healthcare 数据库运行以下查询时,他们将看到 address 字段

MATCH (n:Patient)
 WHERE n.dateOfBirth < date('1972-06-12')
RETURN n.name, n.ssn, n.address, n.dateOfBirth;
结果
+-----------------------------------------------------------------------+
| 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

nurse 角色应该能够执行的另一个主要操作是 doctor 的主要操作:将诊断保存到数据库

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;
结果
+------------------------------------------------------------------+
| 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

执行此操作(否则保留给 doctor 角色)对 nurse 来说涉及更多责任。可能有些护士不应该被托付此选项,这就是为什么您可以将 nurse 角色划分为高级初级护士的原因。目前,Daniel 是一名高级护士。

初级 nurse 的权限

此前,通过组合 doctorreceptionist 角色来创建 nurse 角色导致了不理想的情况,因为 doctor 角色的 DENIED 权限覆盖了 receptionistGRANTED 权限。在这种情况下,目标是增强高级护士的权限,但对于初级护士,他们应该能够执行与高级护士相同的操作,除了将诊断添加到数据库。

为了实现这一点,您可以创建一个专门仅包含额外限制的特殊角色。切换到 system 数据库并运行以下命令

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

然后将此新角色分配给护士 Daniel,以便您可以测试该行为

GRANT ROLE disableDiagnoses TO daniel;

如果您检查 Daniel 现在拥有什么权限,它是两个角色 nursedisableDiagnoses 的组合

SHOW USER daniel PRIVILEGES AS COMMANDS;
结果
+---------------------------------------------------------------------+
| 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 仍然可以看到地址字段,甚至可以执行 doctor 可以执行的诊断调查

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;
结果
+--------------------------------------------------------+
| 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

但当他们尝试将诊断保存到数据库时,他们将被拒绝该操作

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;
结果
+---------------------------------+
| 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].

要将 Daniel 提拔回高级护士,撤销引入限制的角色

REVOKE ROLE disableDiagnoses FROM daniel;

构建自定义管理员角色

itadmin 角色最初是通过复制内置 admin 角色并添加限制来创建的。然而,在某些情况下,拥有 `DENY` 可能不如仅拥有 `GRANT` 方便。相反,您可以从头开始构建管理员角色。

IT 管理员 Tina 能够以 itadmin 身份创建新用户并将他们分配给产品角色,但您可以创建一个更受限制的角色 userManager,并仅授予它适当的权限。切换到 system 数据库并运行以下命令

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

通过撤销 Tina 的 itadmin 角色并改为授予他们 userManager 角色来测试新行为

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

这些是授予 userManager 的权限

  • USER MANAGEMENT 允许创建、更新和删除用户。

  • ROLE MANAGEMENT 允许创建、更新和删除角色,以及为用户分配角色。

  • SHOW PRIVILEGE 允许列出用户的权限。

列出 Tina 的新权限现在应该显示一个比她作为拥有 itadmin 角色的更有权力的管理员时短得多的列表

SHOW USER tina PRIVILEGES AS COMMANDS;
结果
+--------------------------------------------------+
| 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

此处未授予其他权限管理特权。此角色应具有多大的权力将取决于系统的要求。有关要考虑的完整权限列表,请参阅 admin 角色 部分。

现在 Tina 应该能够创建新用户并将他们分配给角色(从 system 数据库)

CREATE USER sally SET PASSWORD 'secretpassword' CHANGE REQUIRED;
GRANT ROLE receptionist TO sally;
SHOW USER sally PRIVILEGES AS COMMANDS;
结果
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