Skip to content

Commit bb73e50

Browse files
Missing documentation on authorization (#237)
* Add clarification around types for relationship properties * Add clarification on reduced filtering of subscriptions * Add missing sections of operations * Add missing authorization documentation * yarn lock * Apply suggestions from code review Co-authored-by: Richard Sill <156673635+rsill-neo4j@users.noreply.github.com> * Update modules/ROOT/pages/security/authorization.adoc Co-authored-by: Richard Sill <156673635+rsill-neo4j@users.noreply.github.com> * Address review comments --------- Co-authored-by: Richard Sill <156673635+rsill-neo4j@users.noreply.github.com>
1 parent 9074c11 commit bb73e50

File tree

5 files changed

+739
-882
lines changed

5 files changed

+739
-882
lines changed

modules/ROOT/pages/security/authorization.adoc

Lines changed: 124 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ For instance, to only require filtering for the reading and aggregating posts:
5656
[source, graphql, indent=0]
5757
----
5858
type Post @authorization(filter: [
59-
{ operations: [READ, AGGREGATE] where: { node: { author: { id: "$jwt.sub" } } } }
59+
{ operations: [READ, AGGREGATE], where: { node: { author: { id: "$jwt.sub" } } } }
6060
]) {
6161
title: String!
6262
content: String!
@@ -106,11 +106,10 @@ Validation can be configured to only be performed on certain operations:
106106

107107
For instance, to only require validation for the update or deletion of a post:
108108

109-
110109
[source, graphql, indent=0]
111110
----
112111
type Post @authorization(validate: [
113-
{ operations: [UPDATE, DELETE] where: { node: { author: { id: "$jwt.sub" } } } }
112+
{ operations: [UPDATE, DELETE], where: { node: { author: { id: "$jwt.sub" } } } }
114113
]) {
115114
title: String!
116115
content: String!
@@ -123,6 +122,98 @@ type Post @authorization(validate: [
123122
In case there is no `operations` argument with a list of operations, the GraphQL Library treats the authorization configuration as if the full list of operations had been provided.
124123
====
125124

125+
=== When
126+
127+
Validation can be configured to only be performed before or after an operation is executed.
128+
This is done using the `when` argument which accepts an array of the following values:
129+
130+
* `BEFORE`
131+
* `AFTER`
132+
133+
Additionally, some operations only support validation either before or after them, which is summarised in this table:
134+
135+
[cols="2,5"]
136+
|===
137+
| `operation` | `when`
138+
139+
| `READ`
140+
| `BEFORE`
141+
142+
| `AGGREGATE`
143+
| `BEFORE`
144+
145+
| `CREATE`
146+
| `AFTER`
147+
148+
| `UPDATE`
149+
| `BEFORE`, `AFTER`
150+
151+
| `DELETE`
152+
| `BEFORE`
153+
154+
| `CREATE_RELATIONSHIP`
155+
| `BEFORE`, `AFTER`
156+
157+
| `DELETE_RELATIONSHIP`
158+
| `BEFORE`, `AFTER`
159+
160+
|===
161+
162+
As an example, let's say you want someone to be able to update a post.
163+
If you want to check that after the update the author of the post is still the current user, do the following:
164+
165+
[source, graphql, indent=0]
166+
----
167+
type Post @authorization(validate: [
168+
{ operations: [UPDATE], when: [AFTER], where: { node: { author: { id: "$jwt.sub" } } } }
169+
]) {
170+
title: String!
171+
content: String!
172+
author: User! @relationship(type: "AUTHORED", direction: IN)
173+
}
174+
----
175+
176+
== Authorization on fields
177+
178+
The `@authorization` directive can be used either on object types or their fields, with the former being used in examples for the most part on this page.
179+
When applied to a field, the authorization rules are only evaluated if the matching operations are performed on that field.
180+
For example, consider a `User` type with a `password` field:
181+
182+
[source, graphql, indent=0]
183+
----
184+
type User {
185+
id: ID!
186+
username: String!
187+
password: String! @authorization(where: [{ operations: [READ, UPDATE], where: { node: { id: "$jwt.sub" } } }])
188+
}
189+
----
190+
191+
When executing the following query, a valid identity is not needed:
192+
193+
[source, graphql, indent=0]
194+
----
195+
{
196+
users {
197+
username
198+
}
199+
}
200+
----
201+
202+
However, consider the following query:
203+
204+
[source, graphql, indent=0]
205+
----
206+
{
207+
users {
208+
username
209+
password
210+
}
211+
}
212+
----
213+
214+
This will require a valid JWT to have been provided with the request, and the matching users will be filtered down according to the JWT subject.
215+
The same applies for attempting to update the `password` field, the update will only apply to the user matching the JWT.
216+
126217

127218
== Authorization without authentication
128219

@@ -147,3 +238,33 @@ type Post @authorization(filter: [
147238
author: User! @relationship(type: "AUTHORED", direction: IN)
148239
}
149240
----
241+
242+
== Ordering of rules
243+
244+
In each ruleset (`filter` and `validate`), rules are joined with an `OR`.
245+
The two rulesets are joined with an `AND`.
246+
247+
For example, the following would allow for the update of a `User` node if the JWT roles claim includes `admin` _or_ if the `locked` property on the node is `false`:
248+
249+
[source, graphql, indent=0]
250+
----
251+
type User @authorization(validate: [
252+
{ operations: [UPDATE], where: { jwt: { roles_INCLUDES: "admin" } } }
253+
{ operations: [UPDATE], where: { node: { locked: false } } }
254+
]) {
255+
id: ID!
256+
locked: Boolean!
257+
}
258+
----
259+
260+
If you want to combine the rule that a user must be an admin with the rule that the `locked` property must be `false` in order to update a `User` node, add them both to the `where` field using `AND` in a single rule:
261+
262+
[source, graphql, indent=0]
263+
----
264+
type User @authorization(validate: [
265+
{ operations: [UPDATE], where: { AND: [{ jwt: { roles_INCLUDES: "admin" } }, { node: { locked: false } }] } }
266+
]) {
267+
id: ID!
268+
locked: Boolean!
269+
}
270+
----

modules/ROOT/pages/security/operations.adoc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,66 @@ query {
3737
}
3838
----
3939

40+
=== Connection
41+
42+
For a connection query, rules with `READ` in the operations are evaluated for any type being read:
43+
44+
[source, graphql, indent=0]
45+
----
46+
query {
47+
moviesConnection {
48+
edges {
49+
node { # READ ON OBJECT Movie
50+
title # READ ON FIELD_DEFINITION Movie.title
51+
actorsConnection {
52+
edges {
53+
node { # READ ON OBJECT Actor
54+
name # READ ON FIELD_DEFINITION Actor.name
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
}
62+
----
63+
64+
=== Aggregation
65+
66+
For an aggregation query, rules with `AGGREGATE` in the operations are evaluated for any type being aggregated:
67+
68+
[source, graphql, indent=0]
69+
----
70+
query {
71+
moviesAggregate { # AGGREGATE ON OBJECT Movie
72+
title { # AGGREGATE ON FIELD_DEFINITION Movie.title
73+
longest
74+
}
75+
}
76+
}
77+
----
78+
79+
The same logic applies to aggregations of nested nodes:
80+
81+
[source, graphql, indent=0]
82+
----
83+
query {
84+
movies {
85+
actorsAggregate { # AGGREGATE ON OBJECT Actor
86+
node {
87+
name { # AGGREGATE ON FIELD_DEFINITION Actor.name
88+
longest
89+
}
90+
}
91+
}
92+
}
93+
}
94+
----
95+
4096
== Mutation
4197

98+
=== Create
99+
42100
For `create` mutations, `CREATE` rules on the object are evaluated for each node created, as well as field definition rules:
43101

44102
[source, graphql, indent=0]
@@ -56,6 +114,8 @@ mutation {
56114
}
57115
----
58116

117+
=== Delete
118+
59119
For single `delete` mutations, rules with `DELETE` on the object are evaluated:
60120

61121
[source, graphql, indent=0]
@@ -81,6 +141,8 @@ mutation {
81141
}
82142
----
83143

144+
=== Update
145+
84146
For a complex `update` mutation with many effects, a variety of rules is evaluated, as well as `READ` rules for the selection set:
85147

86148
[source, graphql, indent=0]

modules/ROOT/pages/security/subscriptions-authorization.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
= Subscriptions authorization
44

55
Subscriptions require their own authorization rules, which are configured with the `@subscriptionsAuthorization` directive.
6-
These rules are different to authorization rules for queries and mutations because they use filtering rules available for subscriptions events.
6+
These rules are different to authorization rules for queries and mutations because they use filtering rules available for subscriptions events.
7+
These filtering rules can only be used to filter against the properties of the nodes impacted by the events.
78

89
All subscriptions authorization rules have an implied requirement for authentication, given that the rules are normally evaluated against values in the JWT payload.
910

modules/ROOT/pages/types/relationships.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ You can add relationship properties to the example in two steps:
6565
. Add a type definition decorated with the `@relationshipProperties` directive, containing the desired relationship properties.
6666
. Add a `properties` argument to both "sides" (or just one side, if you prefer) of the `@relationship` directive which points to the newly defined interface.
6767

68+
Relationship properties fields can only be primitive types or their list variants.
69+
You cannot map complex types such as object types into the types modelling relationship properties.
70+
6871
For example, suppose you want to distinguish which roles an actor played in a movie:
6972

7073
[source, graphql, indent=0]

0 commit comments

Comments
 (0)