From 547d12b1faa3909e7371567f15d758a71d23fa53 Mon Sep 17 00:00:00 2001 From: Hannes Sandberg Date: Fri, 6 Oct 2023 13:05:33 +0200 Subject: [PATCH] add property rules syntax and example to privileges page add property rules example to limitations section add new section for property-based access control recomend to use Block Storage review fixes: limitations page update images for granting/denying privilege syntax Update modules/ROOT/pages/authentication-authorization/manage-privileges.adoc Co-authored-by: Phil Wright <95368282+phil198@users.noreply.github.com> review fixes review fixes review fix review fixes change header in limitations page Update modules/ROOT/pages/authentication-authorization/property-based-access-control.adoc Co-authored-by: Phil Wright <95368282+phil198@users.noreply.github.com> Apply suggestions from code review Co-authored-by: Reneta Popova --- modules/ROOT/content-nav.adoc | 1 + .../privileges_grant_and_deny_syntax.svg | 2 +- .../images/privileges_on_graph_syntax.svg | 2 +- .../limitations.adoc | 60 ++++++++++++--- .../manage-privileges.adoc | 11 ++- .../privileges-reads.adoc | 45 ++++++++++- .../property-based-access-control.adoc | 74 +++++++++++++++++++ 7 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 modules/ROOT/pages/authentication-authorization/property-based-access-control.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index aa94b9c16..c7297018e 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -167,6 +167,7 @@ ** Manage privileges *** xref:authentication-authorization/manage-privileges.adoc[] *** xref:authentication-authorization/privileges-reads.adoc[] +*** xref:authentication-authorization/property-based-access-control.adoc[] *** xref:authentication-authorization/privileges-writes.adoc[] *** xref:authentication-authorization/database-administration.adoc[] *** xref:authentication-authorization/dbms-administration.adoc[] diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax.svg b/modules/ROOT/images/privileges_grant_and_deny_syntax.svg index 156002d3a..5f76cc474 100644 --- a/modules/ROOT/images/privileges_grant_and_deny_syntax.svg +++ b/modules/ROOT/images/privileges_grant_and_deny_syntax.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/modules/ROOT/images/privileges_on_graph_syntax.svg b/modules/ROOT/images/privileges_on_graph_syntax.svg index 321deeb2b..570896e2b 100644 --- a/modules/ROOT/images/privileges_on_graph_syntax.svg +++ b/modules/ROOT/images/privileges_on_graph_syntax.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/modules/ROOT/pages/authentication-authorization/limitations.adoc b/modules/ROOT/pages/authentication-authorization/limitations.adoc index fd1c7e38c..a18ded51a 100644 --- a/modules/ROOT/pages/authentication-authorization/limitations.adoc +++ b/modules/ROOT/pages/authentication-authorization/limitations.adoc @@ -6,7 +6,7 @@ CREATE ROLE users; CREATE ROLE custom; CREATE ROLE restricted; -CREATE ROLE free; +CREATE ROLE unrestricted; ---- //// @@ -279,19 +279,19 @@ will only return label `:A`, because that is the only label for which traversal [[access-control-limitations-db-operations]] -== Security and count store operations +== Security and performance -The rules of a security model may impact some of the database operations. -This means extra security checks are necessary to incur additional data accesses, especially in the case of count store operations. -These are, however, usually very fast lookups and the difference might be noticeable. +The rules of a security model may impact the performance of some database operations. +This is because extra security checks are necessary, and they require additional data access. +For example, count store operations, which are usually fast lookups, may experience notable differences in performance. -See the following security rules that use a `restricted` and a `free` role as an example: +The following example shows how the database behaves when adding security rules to roles `restricted` and `unrestricted`: [source, cypher] ---- GRANT TRAVERSE ON GRAPH * NODES Person TO restricted; DENY TRAVERSE ON GRAPH * NODES Customer TO restricted; -GRANT TRAVERSE ON GRAPH * ELEMENTS * TO free; +GRANT TRAVERSE ON GRAPH * ELEMENTS * TO unrestricted; ---- Now, let's look at what the database needs to do in order to execute the following query: @@ -302,7 +302,7 @@ MATCH (n:Person) RETURN count(n) ---- -For both roles the execution plan will look like this: +For both roles, the execution plan looks like this: ---- +--------------------------+ @@ -319,7 +319,7 @@ The following table illustrates the difference: [%header,cols=2*] |=== -|User with `free` role +|User with `unrestricted` role |User with `restricted` role |The database can access the count store and retrieve the total number of nodes with the label `:Person`. @@ -329,6 +329,46 @@ This is a very quick operation. |The database cannot access the count store because it must make sure that only traversable nodes with the desired label `:Person` are counted. Due to this, each node with the `:Person` label needs to be accessed and examined to make sure that they do not have a deny-listed label, such as `:Customer`. -Due to the additional data accesses that the security checks need to do, this operation will be slower compared to executing the query as an unrestricted user. +So due to the additional data access required by the security checks, this operation will be slower compared to executing the query as an unrestricted user. |=== + +[[property-based-access-control-limitations]] +=== Property-based access control limitations +Extra node-level security checks are necessary when adding security rules based on property rules, and these can have a significant performance impact. +The following example shows how the database behaves when adding security rules to roles `restricted` and `unrestricted`: + +[source, cypher] +---- +GRANT TRAVERSE ON GRAPH * FOR (n:Customer) WHERE n.secret <> true TO restricted; +GRANT TRAVERSE ON GRAPH * ELEMENTS * TO unrestricted; +---- + +When executing query: + +[source, cypher] +---- +MATCH (n:Customer) +RETURN n +---- +For both roles, the execution plan looks like this: +---- ++--------------------------+ +| Operator | ++--------------------------+ +| +ProduceResults | +| | + +| +AllNodesScan | ++--------------------------+ +---- +Internally, however, very different operations need to be executed. +The following table illustrates the difference: +[%header,cols=2*] +|=== +|User with `unrestricted` role +|User with `restricted` role +|The database will scan all nodes and quickly identify accessible nodes based solely on the presence of the `:Customer` label. +This is a relatively quick operation. +|The database will scan all nodes, identify potentially accessible nodes based on the presence of the specified label, and then also access the properties of each of those nodes and inspect their values to ensure the property rule criteria are met (i.e., that `secret` is not set to `true` in this case). +So due to the additional data access required by the security checks, this operation will be slower compared to executing the query as an unrestricted user. +|=== \ No newline at end of file diff --git a/modules/ROOT/pages/authentication-authorization/manage-privileges.adoc b/modules/ROOT/pages/authentication-authorization/manage-privileges.adoc index b97e1b2ec..a740dc5f8 100644 --- a/modules/ROOT/pages/authentication-authorization/manage-privileges.adoc +++ b/modules/ROOT/pages/authentication-authorization/manage-privileges.adoc @@ -2,7 +2,7 @@ [role=enterprise-edition aura-db-enterprise] [[access-control-manage-privileges]] -= RBAC and fine-grained privileges += Role-based access control Role-based access control (_RBAC_) is a method of restricting access to authorized users, by assigning users to specific roles with a particular set of privileges granted to them. Privileges control the access rights to graph elements using a combined allowlist/denylist mechanism. @@ -72,11 +72,14 @@ This can be quite powerful as it allows permissions to be switched from one grap *** `NODES` label (nodes with the specified label(s)). *** `RELATIONSHIPS` type (relationships of the specific type(s)). *** `ELEMENTS` label (both nodes and relationships). +*** `FOR` pattern (nodes that match the pattern). +See xref:authentication-authorization/property-based-access-control.adoc[Property-based access control] for details ** The label or type can be referred with `+*+`, which means all labels or types. ** Multiple labels or types can be specified, comma-separated. ** Defaults to `ELEMENTS` `+*+` if omitted. ** Some of the commands for write privileges do not allow an _entity_ part. See xref:authentication-authorization/privileges-writes.adoc[Write privileges] for details. +** The `FOR` pattern _entity_ is not supported for write privileges. * _role[, ...]_ ** The role or roles to associate the privilege with, comma-separated. @@ -245,7 +248,7 @@ Results will include multiple columns describing the privileges: | STRING | scope -| List of possible scopes for the privilege (`elements`, `nodes`, `relationships`) or null if not applicable. +| List of possible scopes for the privilege (`elements`, `nodes`, `pattern`, `relationships`) or null if not applicable. | LIST OF STRING | description @@ -308,7 +311,7 @@ Lists 10 supported privileges: | "traverse" | NULL | "graph" -| ["elements", "nodes", "relationships"] +| ["elements", "nodes", "pattern", "relationships"] | "enables the specified entities to be found" | "transaction management" @@ -463,7 +466,7 @@ E.g., the entire DBMS, a specific database, a graph, or sub-graph access. | STRING | segment -| The labels, relationship types, procedures, functions, transactions or settings the privilege applies to (if applicable). +| The labels, relationship types, pattern, procedures, functions, transactions or settings the privilege applies to (if applicable). | STRING | role diff --git a/modules/ROOT/pages/authentication-authorization/privileges-reads.adoc b/modules/ROOT/pages/authentication-authorization/privileges-reads.adoc index 62f9ba1c6..09c4c6c67 100644 --- a/modules/ROOT/pages/authentication-authorization/privileges-reads.adoc +++ b/modules/ROOT/pages/authentication-authorization/privileges-reads.adoc @@ -35,11 +35,17 @@ GRANT [IMMUTABLE] TRAVERSE ELEMENT[S] { * | label-or-rel-type[, ...] } | NODE[S] { * | label[, ...] } | RELATIONSHIP[S] { * | rel-type[, ...] } + | FOR pattern ] TO role[, ...] ---- -For example, we can enable users with the role `regularUsers` to find all nodes with the label `Post` in the database `neo4j`: +[NOTE] +==== +For more details about the `pattern` syntax used to express attributes based access control rules, see xref:authentication-authorization/property-based-access-control.adoc[Property-based access control]. +==== + +For example, you can enable users with the role `regularUsers` to find all nodes with the label `Post` in the database `neo4j`: [source, cypher, role=noplay] ---- @@ -56,6 +62,7 @@ DENY [IMMUTABLE] TRAVERSE ELEMENT[S] { * | label-or-rel-type[, ...] } | NODE[S] { * | label[, ...] } | RELATIONSHIP[S] { * | rel-type[, ...] } + | FOR pattern ] TO role[, ...] ---- @@ -67,6 +74,13 @@ For example, we can disable users with the role `regularUsers` from finding all DENY TRAVERSE ON HOME GRAPH NODES Payments TO regularUsers ---- +Although you just granted the role `regularUsers` the right to read all properties on nodes with label `Post`, you may want to make this more fine-grained using xref:authentication-authorization/property-based-access-control.adoc[Property-based access control] to hide the posts with `secret` property set to `true`. +For example: + +[source, cypher, role=noplay] +---- +DENY TRAVERSE ON HOME GRAPH FOR (:Post {secret: true}) TO regularUsers +---- [[access-control-privileges-reads-read]] == The `READ` privilege @@ -82,11 +96,16 @@ GRANT [IMMUTABLE] READ "{" { * | property[, ...] } "}" ELEMENT[S] { * | label-or-rel-type[, ...] } | NODE[S] { * | label[, ...] } | RELATIONSHIP[S] { * | rel-type[, ...] } + | FOR pattern ] TO role[, ...] ---- +[NOTE] +==== +For more details about the `pattern` syntax used to express attributes based access control rules, see xref:authentication-authorization/property-based-access-control.adoc[Property-based access control]. +==== -For example, we can enable user with the role `regularUsers` to read all properties on nodes with the label `Post` in the database `neo4j`. +For example, you can enable users with the role `regularUsers` to read all properties on nodes with the label `Post` in the database `neo4j`. The `+*+` implies that the ability to read all properties also extends to properties that might be added in the future. [source, cypher, role=noplay] @@ -94,6 +113,14 @@ The `+*+` implies that the ability to read all properties also extends to proper GRANT READ { * } ON GRAPH neo4j NODES Post TO regularUsers ---- +To further fine-grained the read access, you can enable users with the role `regularUsers` to read all properties on nodes with the label `Post` that have property `secret` not set to `true` in the database `neo4j`. +For example: + +[source, cypher, role=noplay] +---- +GRANT READ { * } ON GRAPH neo4j FOR (n:Post) WHERE n.secret <> true TO regularUsers +---- + [NOTE] ==== Granting property `READ` access does not imply that the entities with that property can be found. @@ -110,6 +137,7 @@ DENY [IMMUTABLE] READ "{" { * | property[, ...] } "}" ELEMENT[S] { * | label-or-rel-type[, ...] } | NODE[S] { * | label[, ...] } | RELATIONSHIP[S] { * | rel-type[, ...] } + | FOR pattern ] TO role[, ...] ---- @@ -137,9 +165,14 @@ GRANT [IMMUTABLE] MATCH "{" { * | property[, ...] } "}" ELEMENT[S] { * | label-or-rel-type[, ...] } | NODE[S] { * | label[, ...] } | RELATIONSHIP[S] { * | rel-type[, ...] } + | FOR pattern ] TO role[, ...] ---- +[NOTE] +==== +For more details about the `pattern` syntax used to express attributes based access control rules, see xref:authentication-authorization/property-based-access-control.adoc[Property-based access control]. +==== For example if you want to grant the ability to read the properties `language` and `length` for nodes with the label `Message`, as well as the ability to find these nodes to the role `regularUsers`, you can use the following `GRANT MATCH` query: @@ -148,6 +181,13 @@ For example if you want to grant the ability to read the properties `language` a GRANT MATCH { language, length } ON GRAPH neo4j NODES Message TO regularUsers ---- +The following query grants the `regularUsers` role the ability to find `Post` and `Likes` nodes where the `secret` property is set to `false`, as well as reading all their properties. + +[source, cypher, role=noplay] +---- +GRANT MATCH { * } ON GRAPH neo4j FOR (n:Post|Likes) WHERE n.secret = false TO regularUsers +---- + Like all other privileges, the `MATCH` privilege can also be denied. [source, syntax, role="noheader"] @@ -158,6 +198,7 @@ DENY [IMMUTABLE] MATCH "{" { * | property[, ...] } "}" ELEMENT[S] { * | label-or-rel-type[, ...] } | NODE[S] { * | label[, ...] } | RELATIONSHIP[S] { * | rel-type[, ...] } + | FOR pattern ] TO role[, ...] ---- diff --git a/modules/ROOT/pages/authentication-authorization/property-based-access-control.adoc b/modules/ROOT/pages/authentication-authorization/property-based-access-control.adoc new file mode 100644 index 000000000..5fa979384 --- /dev/null +++ b/modules/ROOT/pages/authentication-authorization/property-based-access-control.adoc @@ -0,0 +1,74 @@ +:description: How to use Cypher to manage property-based access control on graphs. + +//// +[source, cypher, role=test-setup] +---- +CREATE ROLE regularUsers; +---- +//// + +[role=enterprise-edition aura-db-enterprise] +[[property-based-access-control]] += Property-based access control + +It is possible to create read privileges that are based on properties of nodes. +Each property-based privilege can only be restricted by a single property. +To specify the property/value conditions of the read privilege the `pattern` syntax described below is used, +for more information about read privilege syntax see xref:authentication-authorization/privileges-reads.adoc[read privilege] page. + +Adding property-based access control may lead to a significant performance overhead in certain scenarios. +See xref:authentication-authorization/limitations.adoc#property-based-access-control-limitations[Limitations] for more detailed information. +To reduce the performance impact, it is recommended to use the Block Storage format as it is better optimized for the kind of read required for the resolution of property-based privileges. + +Some of the factors that can worsen the impact on performance when having property rules are: + +* The number of properties on the nodes concerned (more properties = greater performance impact) +* The number of property-based privileges (more property-based privileges = greater performance impact). +* The type of the privilege: `TRAVERSE` property-based privileges have a greater performance impact than `READ` property-based privileges. +* The type of storage medium in operation. The performance impact of property-based privileges will be considerably amplified by accessing disc storage. + +For performance-critical scenarios, it is recommended to design privileges based on labels. +For more information, see xref:authentication-authorization/privileges-reads.adoc[Read privileges]. + +Pattern syntax: +[source, syntax, role="noheader"] +---- +([var][:label["|" ...]] "{" property: value "}") +| (var[:label["|" ...]]) WHERE [NOT] var.property { = value | <> value | IS NULL | IS NOT NULL } +| (var[:label["|" ...]] WHERE [NOT] var.property { = value | <> value | IS NULL | IS NOT NULL } ) +---- +[NOTE] +==== +For more details about the syntax descriptions, see xref:database-administration/syntax.adoc[Cypher syntax for administration commands]. +==== +You can use this pattern syntax for defining read privileges as follows: + +[source, syntax, role="noheader"] +---- +GRANT ... ON GRAPH ... FOR pattern TO ... +---- + + +.Granting permission to `READ` the `address` property on `Email` or `Website` nodes with domain `exampledomain.com` to role `regularUsers`: +[source, syntax, role="noheader"] +---- +GRANT READ { address } ON GRAPH * FOR (n:Email|Website) WHERE n.domain = 'exampledomain.com' TO regularUsers +---- +Alternatively, you can use the following syntax: +[source, syntax, role="noheader"] +---- +GRANT READ { address } ON GRAPH * FOR (:Email|Website {domain: 'exampledomain.com'}) TO regularUsers +---- + + +.Granting permission to `TRAVERSE` nodes with label `Email` where property `classification` is `NULL` to role `regularUsers`: +[source, syntax, role="noheader"] +---- +GRANT TRAVERSE ON GRAPH * FOR (n:Email) WHERE n.classification IS NULL TO regularUsers +---- + +.Denying permission to `READ` and `TRAVERSE` nodes where the property `classification` is different from `UNCLASSIFIED` to role `regularUsers`: +[source, syntax, role="noheader"] +---- +DENY MATCH {*} ON GRAPH * FOR (n) WHERE n.classification <> 'UNCLASSIFIED' TO regularUsers +----