Skip to content

Commit

Permalink
Property rules documentation (neo4j#1076)
Browse files Browse the repository at this point in the history
Co-authored-by: Reneta Popova <[email protected]>
  • Loading branch information
HannesSandberg and renetapopova committed Jun 28, 2024
1 parent 4838b8b commit db13e3d
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 18 deletions.
1 change: 1 addition & 0 deletions modules/ROOT/content-nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,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[]
Expand Down
2 changes: 1 addition & 1 deletion modules/ROOT/images/privileges_grant_and_deny_syntax.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion modules/ROOT/images/privileges_on_graph_syntax.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 50 additions & 10 deletions modules/ROOT/pages/authentication-authorization/limitations.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
CREATE ROLE users;
CREATE ROLE custom;
CREATE ROLE restricted;
CREATE ROLE free;
CREATE ROLE unrestricted;
----
////

Expand Down Expand Up @@ -334,19 +334,19 @@ In this example, when creating the custom role, connect to `testing` and run `CA


[[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:
Expand All @@ -357,7 +357,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:

----
+--------------------------+
Expand All @@ -374,7 +374,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`.
Expand All @@ -384,6 +384,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.
|===
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
----
Expand All @@ -56,6 +62,7 @@ DENY [IMMUTABLE] TRAVERSE
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
| FOR pattern
]
TO role[, ...]
----
Expand All @@ -67,6 +74,14 @@ 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
----

[NOTE]
====
If a label or a relationship type does not exist in the database, the user cannot use the corresponding privilege until it is created.
Expand All @@ -88,18 +103,31 @@ 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]
----
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.
Expand All @@ -116,6 +144,7 @@ DENY [IMMUTABLE] READ "{" { * | property[, ...] } "}"
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
| FOR pattern
]
TO role[, ...]
----
Expand Down Expand Up @@ -149,9 +178,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:

Expand All @@ -160,6 +194,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"]
Expand All @@ -170,6 +211,7 @@ DENY [IMMUTABLE] MATCH "{" { * | property[, ...] } "}"
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
| FOR pattern
]
TO role[, ...]
----
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
: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].

[IMPORTANT]
====
When using property-based access control, ensure the property used for the rule cannot be modified.
Users who can change this property can affect the granted property-based privileges.
====

Pattern syntax:
[source, syntax, role="noheader"]
----
([var][:label["|" ...]] "{" property: value "}")
| (var[:label["|" ...]]) WHERE [NOT] var.property { { = | <> | > | >= | < | <= } value | IS NULL | IS NOT NULL | IN { "["[value[, ...]]"]" | listParam } }
| (var[:label["|" ...]] WHERE [NOT] var.property { { = | <> | > | >= | < | <= } value | IS NULL | IS NOT NULL | IN { "["[value[, ...]]"]" | listParam } } )
----
[NOTE]
====
For more details about the syntax descriptions, see xref:database-administration/syntax.adoc[Cypher syntax for administration commands].
====
[NOTE]
====
The role does not need to have `READ` privilege for the property used by the property-based privilege.
====
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
----

.Granting permission to `READ` all properties on nodes where the property `securityLevel` is higher than `3` to role `regularUsers`:
[source, syntax, role="noheader"]
----
GRANT READ {*} ON GRAPH * FOR (n) WHERE n.securityLevel > 3 TO regularUsers
----
[NOTE]
====
The role `regularUsers` does not need to have `READ` privilege for the property `securityLevel` used by the property-based privilege.
====

.Denying permission to `READ` all properties on nodes where the property `classification` is not included in the list of `[UNCLASSIFIED, PUBLIC]`
[source, syntax, role="noheader"]
----
DENY READ {*} ON GRAPH * FOR (n) WHERE NOT n.classification IN ['UNCLASSIFIED', 'PUBLIC'] TO regularUsers
----

0 comments on commit db13e3d

Please sign in to comment.