graphql-shield-node23 7.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/cjs/constructors.js +134 -0
- package/dist/cjs/generator.js +205 -0
- package/dist/cjs/index.js +15 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/rules.js +402 -0
- package/dist/cjs/shield.js +52 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils.js +97 -0
- package/dist/cjs/validation.js +84 -0
- package/dist/esm/constructors.js +124 -0
- package/dist/esm/generator.js +201 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/rules.js +366 -0
- package/dist/esm/shield.js +45 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils.js +88 -0
- package/dist/esm/validation.js +79 -0
- package/dist/package.json +47 -0
- package/dist/typings/constructors.d.cts +91 -0
- package/dist/typings/constructors.d.ts +91 -0
- package/dist/typings/generator.d.cts +11 -0
- package/dist/typings/generator.d.ts +11 -0
- package/dist/typings/index.d.cts +3 -0
- package/dist/typings/index.d.ts +3 -0
- package/dist/typings/rules.d.cts +159 -0
- package/dist/typings/rules.d.ts +159 -0
- package/dist/typings/shield.d.cts +11 -0
- package/dist/typings/shield.d.ts +11 -0
- package/dist/typings/types.d.cts +64 -0
- package/dist/typings/types.d.ts +64 -0
- package/dist/typings/utils.d.cts +52 -0
- package/dist/typings/utils.d.ts +52 -0
- package/dist/typings/validation.d.cts +19 -0
- package/dist/typings/validation.d.ts +19 -0
- package/package.json +67 -0
- package/src/constructors.ts +157 -0
- package/src/generator.ts +294 -0
- package/src/index.ts +13 -0
- package/src/rules.ts +521 -0
- package/src/shield.ts +53 -0
- package/src/types.ts +94 -0
- package/src/utils.ts +101 -0
- package/src/validation.ts +90 -0
- package/tests/__snapshots__/input.test.ts.snap +7 -0
- package/tests/cache.test.ts +545 -0
- package/tests/constructors.test.ts +136 -0
- package/tests/fallback.test.ts +618 -0
- package/tests/fragments.test.ts +113 -0
- package/tests/generator.test.ts +356 -0
- package/tests/input.test.ts +63 -0
- package/tests/integration.test.ts +65 -0
- package/tests/logic.test.ts +530 -0
- package/tests/utils.test.ts +55 -0
- package/tests/validation.test.ts +139 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import { graphql, GraphQLResolveInfo } from 'graphql'
|
|
2
|
+
import { applyMiddleware } from 'graphql-middleware'
|
|
3
|
+
import { makeExecutableSchema } from '@graphql-tools/schema'
|
|
4
|
+
|
|
5
|
+
import { shield, rule, allow, deny, and, or, not } from '../src'
|
|
6
|
+
import { LogicRule } from '../src/rules'
|
|
7
|
+
import { chain, race } from '../src/constructors'
|
|
8
|
+
|
|
9
|
+
describe('logic rules', () => {
|
|
10
|
+
test('allow, deny work as expeted', async () => {
|
|
11
|
+
const typeDefs = `
|
|
12
|
+
type Query {
|
|
13
|
+
allow: String
|
|
14
|
+
deny: String
|
|
15
|
+
}
|
|
16
|
+
`
|
|
17
|
+
|
|
18
|
+
const resolvers = {
|
|
19
|
+
Query: {
|
|
20
|
+
allow: () => 'allow',
|
|
21
|
+
deny: () => 'deny',
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
26
|
+
|
|
27
|
+
// Permissions
|
|
28
|
+
const permissions = shield({
|
|
29
|
+
Query: {
|
|
30
|
+
allow: allow,
|
|
31
|
+
deny: deny,
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
36
|
+
|
|
37
|
+
/* Execution */
|
|
38
|
+
|
|
39
|
+
const query = `
|
|
40
|
+
query {
|
|
41
|
+
allow
|
|
42
|
+
deny
|
|
43
|
+
}
|
|
44
|
+
`
|
|
45
|
+
const res = await graphql({
|
|
46
|
+
schema: schemaWithPermissions,
|
|
47
|
+
source: query,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/* Tests */
|
|
51
|
+
|
|
52
|
+
expect(res.data).toEqual({
|
|
53
|
+
allow: 'allow',
|
|
54
|
+
deny: null,
|
|
55
|
+
})
|
|
56
|
+
expect(res.errors?.length).toBe(1)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('and works as expected', async () => {
|
|
60
|
+
const typeDefs = `
|
|
61
|
+
type Query {
|
|
62
|
+
allow: String
|
|
63
|
+
deny: String
|
|
64
|
+
ruleError: String
|
|
65
|
+
}
|
|
66
|
+
`
|
|
67
|
+
|
|
68
|
+
const resolvers = {
|
|
69
|
+
Query: {
|
|
70
|
+
allow: () => 'allow',
|
|
71
|
+
deny: () => 'deny',
|
|
72
|
+
ruleError: () => 'ruleError',
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
77
|
+
|
|
78
|
+
/* Permissions */
|
|
79
|
+
|
|
80
|
+
const ruleWithError = rule()(async () => {
|
|
81
|
+
throw new Error()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const permissions = shield({
|
|
85
|
+
Query: {
|
|
86
|
+
allow: and(allow, allow),
|
|
87
|
+
deny: and(allow, deny),
|
|
88
|
+
ruleError: and(allow, ruleWithError),
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
93
|
+
|
|
94
|
+
/* Execution */
|
|
95
|
+
|
|
96
|
+
const query = `
|
|
97
|
+
query {
|
|
98
|
+
allow
|
|
99
|
+
deny
|
|
100
|
+
ruleError
|
|
101
|
+
}
|
|
102
|
+
`
|
|
103
|
+
const res = await graphql({
|
|
104
|
+
schema: schemaWithPermissions,
|
|
105
|
+
source: query,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
/* Tests */
|
|
109
|
+
|
|
110
|
+
expect(res.data).toEqual({
|
|
111
|
+
allow: 'allow',
|
|
112
|
+
deny: null,
|
|
113
|
+
ruleError: null,
|
|
114
|
+
})
|
|
115
|
+
expect(res.errors?.length).toBe(2)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('chain works as expected', async () => {
|
|
119
|
+
const typeDefs = `
|
|
120
|
+
type Query {
|
|
121
|
+
allow: String
|
|
122
|
+
deny: String
|
|
123
|
+
ruleError: String
|
|
124
|
+
}
|
|
125
|
+
`
|
|
126
|
+
|
|
127
|
+
const resolvers = {
|
|
128
|
+
Query: {
|
|
129
|
+
allow: () => 'allow',
|
|
130
|
+
deny: () => 'deny',
|
|
131
|
+
ruleError: () => 'error',
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
136
|
+
|
|
137
|
+
/* Permissions */
|
|
138
|
+
|
|
139
|
+
let allowRuleSequence: string[] = []
|
|
140
|
+
const allowRuleA = rule()(() => {
|
|
141
|
+
allowRuleSequence.push('A')
|
|
142
|
+
return true
|
|
143
|
+
})
|
|
144
|
+
const allowRuleB = rule()(() => {
|
|
145
|
+
allowRuleSequence.push('B')
|
|
146
|
+
return true
|
|
147
|
+
})
|
|
148
|
+
const allowRuleC = rule()(() => {
|
|
149
|
+
allowRuleSequence.push('C')
|
|
150
|
+
return true
|
|
151
|
+
})
|
|
152
|
+
let denyRuleCount = 0
|
|
153
|
+
const denyRule = rule({})(() => {
|
|
154
|
+
denyRuleCount += 1
|
|
155
|
+
return false
|
|
156
|
+
})
|
|
157
|
+
let ruleWithErrorCount = 0
|
|
158
|
+
const ruleWithError = rule()(() => {
|
|
159
|
+
ruleWithErrorCount += 1
|
|
160
|
+
throw new Error('error')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
const permissions = shield({
|
|
164
|
+
Query: {
|
|
165
|
+
allow: chain(allowRuleA, allowRuleB, allowRuleC),
|
|
166
|
+
deny: chain(denyRule, denyRule, denyRule),
|
|
167
|
+
ruleError: chain(ruleWithError, ruleWithError, ruleWithError),
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
172
|
+
|
|
173
|
+
/* Execution */
|
|
174
|
+
|
|
175
|
+
const query = `
|
|
176
|
+
query {
|
|
177
|
+
allow
|
|
178
|
+
deny
|
|
179
|
+
ruleError
|
|
180
|
+
}
|
|
181
|
+
`
|
|
182
|
+
const res = await graphql({
|
|
183
|
+
schema: schemaWithPermissions,
|
|
184
|
+
source: query,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
/* Tests */
|
|
188
|
+
|
|
189
|
+
expect(res.data).toEqual({
|
|
190
|
+
allow: 'allow',
|
|
191
|
+
deny: null,
|
|
192
|
+
ruleError: null,
|
|
193
|
+
})
|
|
194
|
+
expect(allowRuleSequence.toString()).toEqual(['A', 'B', 'C'].toString())
|
|
195
|
+
expect(denyRuleCount).toEqual(1)
|
|
196
|
+
expect(ruleWithErrorCount).toEqual(1)
|
|
197
|
+
expect(res.errors?.length).toBe(2)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('race chain works as expected', async () => {
|
|
201
|
+
const typeDefs = `
|
|
202
|
+
type Query {
|
|
203
|
+
allow: String
|
|
204
|
+
deny: String
|
|
205
|
+
ruleError: String
|
|
206
|
+
}
|
|
207
|
+
`
|
|
208
|
+
|
|
209
|
+
const resolvers = {
|
|
210
|
+
Query: {
|
|
211
|
+
allow: () => 'allow',
|
|
212
|
+
deny: () => 'deny',
|
|
213
|
+
ruleError: () => 'error',
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
218
|
+
|
|
219
|
+
/* Permissions */
|
|
220
|
+
|
|
221
|
+
let allowRuleSequence: string[] = []
|
|
222
|
+
const denyRuleA = rule()(() => {
|
|
223
|
+
allowRuleSequence.push('A')
|
|
224
|
+
return false
|
|
225
|
+
})
|
|
226
|
+
const allowRuleB = rule()(() => {
|
|
227
|
+
allowRuleSequence.push('B')
|
|
228
|
+
return true
|
|
229
|
+
})
|
|
230
|
+
const allowRuleC = rule()(() => {
|
|
231
|
+
allowRuleSequence.push('C')
|
|
232
|
+
return true
|
|
233
|
+
})
|
|
234
|
+
let denyRuleCount = 0
|
|
235
|
+
const denyRule = rule({})(() => {
|
|
236
|
+
denyRuleCount += 1
|
|
237
|
+
return false
|
|
238
|
+
})
|
|
239
|
+
let ruleWithErrorCount = 0
|
|
240
|
+
const ruleWithError = rule()(() => {
|
|
241
|
+
ruleWithErrorCount += 1
|
|
242
|
+
throw new Error('error')
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
const permissions = shield({
|
|
246
|
+
Query: {
|
|
247
|
+
allow: race(denyRuleA, allowRuleB, allowRuleC),
|
|
248
|
+
deny: race(denyRule, denyRule, denyRule),
|
|
249
|
+
ruleError: race(ruleWithError, ruleWithError, ruleWithError),
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
254
|
+
|
|
255
|
+
/* Execution */
|
|
256
|
+
|
|
257
|
+
const query = `
|
|
258
|
+
query {
|
|
259
|
+
allow
|
|
260
|
+
deny
|
|
261
|
+
ruleError
|
|
262
|
+
}
|
|
263
|
+
`
|
|
264
|
+
const res = await graphql({
|
|
265
|
+
schema: schemaWithPermissions,
|
|
266
|
+
source: query,
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
/* Tests */
|
|
270
|
+
|
|
271
|
+
expect(res.data).toEqual({
|
|
272
|
+
allow: 'allow',
|
|
273
|
+
deny: null,
|
|
274
|
+
ruleError: null,
|
|
275
|
+
})
|
|
276
|
+
expect(allowRuleSequence.toString()).toEqual(['A', 'B'].toString())
|
|
277
|
+
expect(denyRuleCount).toEqual(3)
|
|
278
|
+
expect(ruleWithErrorCount).toEqual(3)
|
|
279
|
+
expect(res.errors?.length).toBe(2)
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
test('or works as expected', async () => {
|
|
283
|
+
const typeDefs = `
|
|
284
|
+
type Query {
|
|
285
|
+
allow: String
|
|
286
|
+
deny: String
|
|
287
|
+
}
|
|
288
|
+
`
|
|
289
|
+
|
|
290
|
+
const resolvers = {
|
|
291
|
+
Query: {
|
|
292
|
+
allow: () => 'allow',
|
|
293
|
+
deny: () => 'deny',
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
298
|
+
|
|
299
|
+
/* Permissions */
|
|
300
|
+
|
|
301
|
+
const permissions = shield({
|
|
302
|
+
Query: {
|
|
303
|
+
allow: or(allow, deny),
|
|
304
|
+
deny: or(deny, deny),
|
|
305
|
+
},
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
309
|
+
|
|
310
|
+
/* Execution */
|
|
311
|
+
|
|
312
|
+
const query = `
|
|
313
|
+
query {
|
|
314
|
+
allow
|
|
315
|
+
deny
|
|
316
|
+
}
|
|
317
|
+
`
|
|
318
|
+
const res = await graphql({
|
|
319
|
+
schema: schemaWithPermissions,
|
|
320
|
+
source: query,
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
/* Tests */
|
|
324
|
+
|
|
325
|
+
expect(res.data).toEqual({
|
|
326
|
+
allow: 'allow',
|
|
327
|
+
deny: null,
|
|
328
|
+
})
|
|
329
|
+
expect(res.errors?.length).toBe(1)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
test('not works as expected', async () => {
|
|
333
|
+
const typeDefs = `
|
|
334
|
+
type Query {
|
|
335
|
+
allow: String
|
|
336
|
+
deny: String
|
|
337
|
+
ruleError: String
|
|
338
|
+
resolverError: String
|
|
339
|
+
customRuleError: String
|
|
340
|
+
customRuleErrorString: String
|
|
341
|
+
}
|
|
342
|
+
`
|
|
343
|
+
|
|
344
|
+
const resolvers = {
|
|
345
|
+
Query: {
|
|
346
|
+
allow: () => 'allow',
|
|
347
|
+
deny: () => 'deny',
|
|
348
|
+
ruleError: () => 'ruleError',
|
|
349
|
+
resolverError: () => {
|
|
350
|
+
throw new Error()
|
|
351
|
+
},
|
|
352
|
+
customRuleError: () => 'customRuleError',
|
|
353
|
+
customRuleErrorString: () => 'customRuleErrorString',
|
|
354
|
+
},
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
358
|
+
|
|
359
|
+
/* Permissions */
|
|
360
|
+
|
|
361
|
+
const ruleWithError = rule()(async () => {
|
|
362
|
+
throw new Error()
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
const ruleWithCustomError = rule()(async () => {
|
|
366
|
+
return new Error('error_pass')
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
const ruleWithCustomErrorString = rule()(async () => {
|
|
370
|
+
return 'error_string_pass'
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
const permissions = shield({
|
|
374
|
+
Query: {
|
|
375
|
+
allow: not(deny),
|
|
376
|
+
deny: not(allow),
|
|
377
|
+
ruleError: not(ruleWithError),
|
|
378
|
+
resolverError: not(allow),
|
|
379
|
+
customRuleError: not(ruleWithCustomError),
|
|
380
|
+
customRuleErrorString: not(ruleWithCustomErrorString),
|
|
381
|
+
},
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
385
|
+
|
|
386
|
+
/* Execution */
|
|
387
|
+
|
|
388
|
+
const query = `
|
|
389
|
+
query {
|
|
390
|
+
allow
|
|
391
|
+
deny
|
|
392
|
+
ruleError
|
|
393
|
+
resolverError
|
|
394
|
+
customRuleError
|
|
395
|
+
customRuleErrorString
|
|
396
|
+
}
|
|
397
|
+
`
|
|
398
|
+
const res = await graphql({
|
|
399
|
+
schema: schemaWithPermissions,
|
|
400
|
+
source: query,
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
expect(res.data).toEqual({
|
|
404
|
+
allow: 'allow',
|
|
405
|
+
deny: null,
|
|
406
|
+
ruleError: 'ruleError',
|
|
407
|
+
resolverError: null,
|
|
408
|
+
customRuleError: 'customRuleError',
|
|
409
|
+
customRuleErrorString: 'customRuleErrorString',
|
|
410
|
+
})
|
|
411
|
+
expect(res.errors?.map((err) => err.message)).toEqual([
|
|
412
|
+
'Not Authorised!',
|
|
413
|
+
'Not Authorised!',
|
|
414
|
+
])
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
test('not returns custom error', async () => {
|
|
418
|
+
const typeDefs = `
|
|
419
|
+
type Query {
|
|
420
|
+
not: String
|
|
421
|
+
}
|
|
422
|
+
`
|
|
423
|
+
|
|
424
|
+
const resolvers = {
|
|
425
|
+
Query: {
|
|
426
|
+
not: () => 'not',
|
|
427
|
+
},
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
431
|
+
|
|
432
|
+
/* Permissions */
|
|
433
|
+
|
|
434
|
+
const permissions = shield({
|
|
435
|
+
Query: {
|
|
436
|
+
not: not(allow, 'This is a custom not message.'),
|
|
437
|
+
},
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
441
|
+
|
|
442
|
+
/* Execution */
|
|
443
|
+
|
|
444
|
+
const query = `
|
|
445
|
+
query {
|
|
446
|
+
not
|
|
447
|
+
}
|
|
448
|
+
`
|
|
449
|
+
const res = await graphql({
|
|
450
|
+
schema: schemaWithPermissions,
|
|
451
|
+
source: query,
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
expect(res.data).toEqual({
|
|
455
|
+
not: null,
|
|
456
|
+
})
|
|
457
|
+
expect(res.errors?.map((err) => err.message)).toEqual([
|
|
458
|
+
'This is a custom not message.',
|
|
459
|
+
])
|
|
460
|
+
})
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
describe('internal execution', () => {
|
|
464
|
+
test('logic rule by default resolves to false', async () => {
|
|
465
|
+
const rule = new LogicRule([])
|
|
466
|
+
|
|
467
|
+
const res = await rule.resolve(
|
|
468
|
+
{},
|
|
469
|
+
{},
|
|
470
|
+
{ _shield: { cache: {} } },
|
|
471
|
+
{} as GraphQLResolveInfo,
|
|
472
|
+
{
|
|
473
|
+
allowExternalErrors: false,
|
|
474
|
+
debug: false,
|
|
475
|
+
fallbackRule: allow,
|
|
476
|
+
fallbackError: new Error(),
|
|
477
|
+
hashFunction: () => `${Math.random()}`,
|
|
478
|
+
},
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
expect(res).toBeFalsy()
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
test('rule prevents access when access not permited', async () => {
|
|
485
|
+
const typeDefs = `
|
|
486
|
+
type Query {
|
|
487
|
+
deny: String
|
|
488
|
+
}
|
|
489
|
+
`
|
|
490
|
+
|
|
491
|
+
const resolvers = {
|
|
492
|
+
Query: {
|
|
493
|
+
deny: () => 'deny',
|
|
494
|
+
},
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
498
|
+
|
|
499
|
+
/* Permissions */
|
|
500
|
+
|
|
501
|
+
const ruleDeny = rule()(() => false)
|
|
502
|
+
|
|
503
|
+
const permissions = shield({
|
|
504
|
+
Query: {
|
|
505
|
+
deny: ruleDeny,
|
|
506
|
+
},
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
510
|
+
|
|
511
|
+
/* Execution */
|
|
512
|
+
|
|
513
|
+
const query = `
|
|
514
|
+
query {
|
|
515
|
+
deny
|
|
516
|
+
}
|
|
517
|
+
`
|
|
518
|
+
const res = await graphql({
|
|
519
|
+
schema: schemaWithPermissions,
|
|
520
|
+
source: query,
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
/* Tests */
|
|
524
|
+
|
|
525
|
+
expect(res.data).toEqual({
|
|
526
|
+
deny: null,
|
|
527
|
+
})
|
|
528
|
+
expect(res.errors?.length).toBe(1)
|
|
529
|
+
})
|
|
530
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRule,
|
|
3
|
+
isLogicRule,
|
|
4
|
+
isRuleFieldMap,
|
|
5
|
+
isRuleFunction,
|
|
6
|
+
withDefault,
|
|
7
|
+
} from '../src/utils'
|
|
8
|
+
import { rule, and } from '../src/constructors'
|
|
9
|
+
|
|
10
|
+
import { testSimpleRule, testLogicRule } from 'graphql-shield-rules'
|
|
11
|
+
|
|
12
|
+
describe('type identifiers', () => {
|
|
13
|
+
test('isRuleFunction finds rule function.', async () => {
|
|
14
|
+
expect(isRuleFunction(rule()(() => true))).toBeTruthy()
|
|
15
|
+
expect(isRuleFunction(and())).toBeTruthy()
|
|
16
|
+
expect(isRuleFunction(false)).toBeFalsy()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('isRule finds rule.', async () => {
|
|
20
|
+
expect(isRule(rule()(() => true))).toBeTruthy()
|
|
21
|
+
expect(isRule(and())).toBeFalsy()
|
|
22
|
+
expect(isRule(false)).toBeFalsy()
|
|
23
|
+
expect(isRule(testSimpleRule)).toBeTruthy()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('isLogicRule finds logic rule.', async () => {
|
|
27
|
+
expect(isLogicRule(and())).toBeTruthy()
|
|
28
|
+
expect(isLogicRule(rule()(() => true))).toBeFalsy()
|
|
29
|
+
expect(isLogicRule(false)).toBeFalsy()
|
|
30
|
+
expect(isLogicRule(testLogicRule)).toBeTruthy()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('isRuleFieldMap finds rule field map.', async () => {
|
|
34
|
+
expect(
|
|
35
|
+
isRuleFieldMap({
|
|
36
|
+
foo: rule()(() => true),
|
|
37
|
+
bar: and(),
|
|
38
|
+
}),
|
|
39
|
+
).toBeTruthy()
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
isRuleFieldMap({
|
|
43
|
+
foo: rule()(() => true),
|
|
44
|
+
bar: false,
|
|
45
|
+
}),
|
|
46
|
+
).toBeFalsy()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('helper functions', () => {
|
|
51
|
+
test('withDefault returns correct value', async () => {
|
|
52
|
+
expect(withDefault('pass')(undefined)).toBe('pass')
|
|
53
|
+
expect(withDefault('fail')('pass')).toBe('pass')
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { applyMiddleware } from 'graphql-middleware'
|
|
2
|
+
import { makeExecutableSchema } from '@graphql-tools/schema'
|
|
3
|
+
import { validateRuleTree } from '../src/validation'
|
|
4
|
+
import { shield, rule, allow } from '../src/'
|
|
5
|
+
import { and } from '../src/constructors'
|
|
6
|
+
|
|
7
|
+
describe('correctly helps developer', () => {
|
|
8
|
+
test('Finds a type missing in schema and warns developer.', async () => {
|
|
9
|
+
/* Schema */
|
|
10
|
+
|
|
11
|
+
const typeDefs = `
|
|
12
|
+
type Query {
|
|
13
|
+
a: String!
|
|
14
|
+
}
|
|
15
|
+
`
|
|
16
|
+
|
|
17
|
+
const schema = makeExecutableSchema({
|
|
18
|
+
typeDefs,
|
|
19
|
+
resolvers: {},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// Permissions
|
|
23
|
+
|
|
24
|
+
const permissions = shield({
|
|
25
|
+
Query: allow,
|
|
26
|
+
Fail1: allow,
|
|
27
|
+
Fail2: allow,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
expect(() => {
|
|
31
|
+
applyMiddleware(schema, permissions)
|
|
32
|
+
}).toThrow(
|
|
33
|
+
`It seems like you have applied rules to Fail1, Fail2 types but Shield cannot find them in your schema.`,
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('Finds the fields missing in schema and warns developer.', async () => {
|
|
38
|
+
// Schema
|
|
39
|
+
const typeDefs = `
|
|
40
|
+
type Query {
|
|
41
|
+
a: String!
|
|
42
|
+
}
|
|
43
|
+
`
|
|
44
|
+
|
|
45
|
+
const schema = makeExecutableSchema({
|
|
46
|
+
typeDefs,
|
|
47
|
+
resolvers: {},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Permissions
|
|
51
|
+
|
|
52
|
+
const permissions = shield({
|
|
53
|
+
Query: {
|
|
54
|
+
a: allow,
|
|
55
|
+
b: allow,
|
|
56
|
+
c: allow,
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
expect(() => {
|
|
61
|
+
applyMiddleware(schema, permissions)
|
|
62
|
+
}).toThrow(
|
|
63
|
+
'It seems like you have applied rules to Query.b, Query.c fields but Shield cannot find them in your schema.',
|
|
64
|
+
)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('rule tree validation', () => {
|
|
69
|
+
test('validates rules correctly', async () => {
|
|
70
|
+
/* Rules */
|
|
71
|
+
|
|
72
|
+
const rule1 = rule('one')(() => true)
|
|
73
|
+
const rule12 = rule('one')(() => true)
|
|
74
|
+
const rule2 = rule('two')(() => true)
|
|
75
|
+
const rule22 = rule('two')(() => true)
|
|
76
|
+
const rule3 = rule()(() => true)
|
|
77
|
+
const rule4 = rule()(() => true)
|
|
78
|
+
|
|
79
|
+
const correctRuleTree = {
|
|
80
|
+
Query: {
|
|
81
|
+
foo: rule1,
|
|
82
|
+
bar: rule2,
|
|
83
|
+
},
|
|
84
|
+
Mutation: rule3,
|
|
85
|
+
Bar: rule4,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const incorrectRuleTree = {
|
|
89
|
+
Query: {
|
|
90
|
+
foo: rule1,
|
|
91
|
+
bar: rule12,
|
|
92
|
+
qux: rule2,
|
|
93
|
+
foobarqux: rule22,
|
|
94
|
+
quxbarfoo: and(rule1, rule12),
|
|
95
|
+
},
|
|
96
|
+
Mutation: rule3,
|
|
97
|
+
Bar: rule4,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Tests */
|
|
101
|
+
|
|
102
|
+
expect(validateRuleTree(correctRuleTree)).toEqual({ status: 'ok' })
|
|
103
|
+
expect(validateRuleTree(incorrectRuleTree)).toEqual({
|
|
104
|
+
status: 'err',
|
|
105
|
+
message: `There seem to be multiple definitions of these rules: one, two`,
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('shield works as expected', () => {
|
|
111
|
+
test('throws an error on invalid schema', async () => {
|
|
112
|
+
/* Rules */
|
|
113
|
+
|
|
114
|
+
const rule1 = rule('one')(() => true)
|
|
115
|
+
const rule12 = rule('one')(() => true)
|
|
116
|
+
const rule2 = rule('two')(() => true)
|
|
117
|
+
const rule22 = rule('two')(() => true)
|
|
118
|
+
const rule3 = rule()(() => true)
|
|
119
|
+
const rule4 = rule()(() => true)
|
|
120
|
+
|
|
121
|
+
const incorrectRuleTree = {
|
|
122
|
+
Query: {
|
|
123
|
+
foo: rule1,
|
|
124
|
+
bar: rule12,
|
|
125
|
+
qux: rule2,
|
|
126
|
+
foobarqux: rule22,
|
|
127
|
+
quxbarfoo: and(rule1, rule12),
|
|
128
|
+
},
|
|
129
|
+
Mutation: rule3,
|
|
130
|
+
Bar: rule4,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Tests */
|
|
134
|
+
|
|
135
|
+
expect(() => {
|
|
136
|
+
shield(incorrectRuleTree)
|
|
137
|
+
}).toThrow(`There seem to be multiple definitions of these rules: one, two`)
|
|
138
|
+
})
|
|
139
|
+
})
|