Skip to content

Commit 6891e80

Browse files
committed
adjusted structure, explained directives
1 parent 671cbec commit 6891e80

File tree

1 file changed

+74
-69
lines changed

1 file changed

+74
-69
lines changed

modules/ROOT/pages/security/securing-a-graphql-api.adoc

Lines changed: 74 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ type ordersProperties @relationshipProperties {
5858

5959
== Security-related directives
6060

61-
The GraphQL Library has several directives dedicated to security: `@authentication` and `@authorization`, as well as `@jwt` and `@jwtClaim`.
62-
The `@selectable` and `@settable` directives can be used to control accessibility of data fields through certain operations.
61+
The GraphQL Library has several directives dedicated to security: xref:security/authentication.adoc[`@authentication`] and xref:security/authorization.adoc[`@authorization`], as well as `@jwt` and `@jwtClaim`.
62+
The xref:directives/schema-configuration/field-configuration.adoc#_selectable[`@selectable`] and xref:directives/schema-configuration/field-configuration.adoc#_settable[`@settable`] directives can be used to control accessibility of data fields through certain operations.
6363

6464

6565
=== Authentication
@@ -225,8 +225,43 @@ Also see <<best-practice-internal-errors>> on this page.
225225

226226
=== `@selectable` and `@settable`
227227

228+
To restrict access through operations directly, you can use the xref:directives/schema-configuration/field-configuration.adoc#_selectable[`@selectable`] and xref:directives/schema-configuration/field-configuration.adoc#_settable[`@settable`] directives, for example:
229+
230+
[source, graphql, indent=0]
231+
----
232+
type Customer
233+
@node
234+
@authentication(operations: [DELETE], jwt: { roles: { includes: "admin" } })
235+
@authorization(
236+
filter: [
237+
{ operations: [READ], where: { node: { customerID: { eq: "$jwt.customerID" } } } }
238+
{ where: { jwt: { roles: { includes: "admin" } } } }
239+
]
240+
) {
241+
contactName: String!
242+
sensitiveData: String! @selectable(onRead: false, onAggregate: false)
243+
createdAt: DateTime! @settable(onCreate: true, onUpdate: false)
244+
adminNotes: [String!]! @authorization(validate: [{ where: { jwt: { roles: { includes: "admin" } } } }])
245+
customerID: ID! @id
246+
orders: [Order!]! @relationship(type: "PURCHASED", direction: OUT)
247+
}
248+
----
249+
250+
The `sensitiveData` field is neither available for queries nor for subscriptions nor for aggregations.
251+
The `createdAt` field can be set when a new customer is created, but it cannot be updated.
252+
253+
254+
=== Full example
255+
256+
Here is the full set of type definitions extended with security-related directives:
257+
228258
[source, graphql, indent=0]
229259
----
260+
type JWT @jwt {
261+
roles: [String!]!
262+
customerID: String! @jwtClaim(path: "sub")
263+
}
264+
230265
type Customer
231266
@node
232267
@authentication(operations: [DELETE], jwt: { roles: { includes: "admin" } })
@@ -243,6 +278,43 @@ type Customer
243278
customerID: ID! @id
244279
orders: [Order!]! @relationship(type: "PURCHASED", direction: OUT)
245280
}
281+
282+
type Order
283+
@node
284+
@authentication(operations: [UPDATE, DELETE], jwt: { roles: { includes: "admin" } })
285+
@authorization(
286+
filter: [
287+
{ where: { node: { customer: { all: { customerID: { eq: "$jwt.customerID" } } } } } }
288+
{ where: { jwt: { roles: { includes: "admin" } } } }
289+
]
290+
) {
291+
orderID: ID! @id
292+
customer: [Customer!]! @relationship(type: "PURCHASED", direction: IN)
293+
products: [Product!]! @relationship(type: "ORDERS", direction: OUT, properties: "ordersProperties")
294+
}
295+
296+
type Product @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
297+
productName: String!
298+
category: [Category!]! @relationship(type: "PART_OF", direction: OUT)
299+
orders: [Product!]! @relationship(type: "ORDERS", direction: IN, properties: "ordersProperties")
300+
supplier: [Supplier!]! @relationship(type: "SUPPLIES", direction: IN)
301+
}
302+
303+
type Category @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
304+
categoryName: String!
305+
products: [Product!]! @relationship(type: "PART_OF", direction: IN)
306+
}
307+
308+
type Supplier @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
309+
supplierID: ID! @id
310+
companyName: String!
311+
products: [Product!]! @relationship(type: "SUPPLIES", direction: OUT)
312+
}
313+
314+
type ordersProperties @relationshipProperties {
315+
unitPrice: Float!
316+
quantity: Int!
317+
}
246318
----
247319

248320

@@ -434,73 +506,6 @@ Follow the input validation methods summarized in the link:https://cheatsheetser
434506

435507

436508

437-
== Full example
438-
439-
Here is the full set of type definitions extended with security-related directives:
440-
441-
[source, graphql, indent=0]
442-
----
443-
type JWT @jwt {
444-
roles: [String!]!
445-
customerID: String! @jwtClaim(path: "sub")
446-
}
447-
448-
type Customer
449-
@node
450-
@authentication(operations: [DELETE], jwt: { roles: { includes: "admin" } })
451-
@authorization(
452-
filter: [
453-
{ operations: [READ], where: { node: { customerID: { eq: "$jwt.customerID" } } } }
454-
{ where: { jwt: { roles: { includes: "admin" } } } }
455-
]
456-
) {
457-
contactName: String!
458-
sensitiveData: String! @selectable(onRead: false, onAggregate: false)
459-
createdAt: DateTime! @settable(onCreate: true, onUpdate: false)
460-
adminNotes: [String!]! @authorization(validate: [{ where: { jwt: { roles: { includes: "admin" } } } }])
461-
customerID: ID! @id
462-
orders: [Order!]! @relationship(type: "PURCHASED", direction: OUT)
463-
}
464-
465-
type Order
466-
@node
467-
@authentication(operations: [UPDATE, DELETE], jwt: { roles: { includes: "admin" } })
468-
@authorization(
469-
filter: [
470-
{ where: { node: { customer: { all: { customerID: { eq: "$jwt.customerID" } } } } } }
471-
{ where: { jwt: { roles: { includes: "admin" } } } }
472-
]
473-
) {
474-
orderID: ID! @id
475-
customer: [Customer!]! @relationship(type: "PURCHASED", direction: IN)
476-
products: [Product!]! @relationship(type: "ORDERS", direction: OUT, properties: "ordersProperties")
477-
}
478-
479-
type Product @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
480-
productName: String!
481-
category: [Category!]! @relationship(type: "PART_OF", direction: OUT)
482-
orders: [Product!]! @relationship(type: "ORDERS", direction: IN, properties: "ordersProperties")
483-
supplier: [Supplier!]! @relationship(type: "SUPPLIES", direction: IN)
484-
}
485-
486-
type Category @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
487-
categoryName: String!
488-
products: [Product!]! @relationship(type: "PART_OF", direction: IN)
489-
}
490-
491-
type Supplier @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
492-
supplierID: ID! @id
493-
companyName: String!
494-
products: [Product!]! @relationship(type: "SUPPLIES", direction: OUT)
495-
}
496-
497-
type ordersProperties @relationshipProperties {
498-
unitPrice: Float!
499-
quantity: Int!
500-
}
501-
----
502-
503-
504509
== Further reading
505510

506511
Neo4j has a link:https://neo4j.com/docs/operations-manual/current/authentication-authorization/manage-privileges/[Role-based access control] mechanism that can be leveraged to increase security even further.

0 commit comments

Comments
 (0)