mercurius-auth
Advanced tools
Comparing version 1.4.0 to 1.5.0
@@ -29,4 +29,4 @@ 'use strict' | ||
if (Array.isArray(astNode.directives) && astNode.directives.length > 0) { | ||
const authDirective = astNode.directives.find(directive => directive.name.value === this[kAuthDirective]) | ||
if (typeof authDirective !== 'undefined') { | ||
const authDirective = astNode.directives.filter(directive => directive.name.value === this[kAuthDirective]) | ||
if (authDirective.length > 0) { | ||
return authDirective | ||
@@ -118,19 +118,26 @@ } | ||
if (typeof schemaType !== 'undefined' && typeof schemaType.getFields === 'function') { | ||
for (const [fieldName, fieldPolicy] of Object.entries(typePolicy)) { | ||
if (fieldName === '__typePolicy') { | ||
if (typeof schemaType.resolveReference === 'function') { | ||
// If type is a reference resolver, we wrap this function | ||
const originalResolveReference = schemaType.resolveReference | ||
schemaType.resolveReference = this[kMakeProtectedResolver](fieldPolicy, originalResolveReference) | ||
for (let [fieldName, fieldPolicies] of Object.entries(typePolicy)) { | ||
// this branch of code handles both internal and external policies registration | ||
if (!Array.isArray(fieldPolicies)) { | ||
fieldPolicies = [fieldPolicies] | ||
} | ||
for (const fieldPolicy of fieldPolicies) { | ||
if (fieldName === '__typePolicy') { | ||
if (typeof schemaType.resolveReference === 'function') { | ||
// If type is a reference resolver, we wrap this function | ||
const originalResolveReference = schemaType.resolveReference | ||
schemaType.resolveReference = this[kMakeProtectedResolver](fieldPolicy, originalResolveReference) | ||
} else { | ||
// Wrap each field for a protected schema type | ||
for (const schemaTypeField of Object.values(schemaType.getFields())) { | ||
this[kWrapFieldResolver](schemaTypeField, fieldPolicy) | ||
} | ||
} | ||
} else { | ||
// Wrap each field for a protected schema type | ||
for (const schemaTypeField of Object.values(schemaType.getFields())) { | ||
const schemaTypeField = schemaType.getFields()[fieldName] | ||
if (typeof schemaTypeField !== 'undefined') { | ||
this[kWrapFieldResolver](schemaTypeField, fieldPolicy) | ||
} | ||
} | ||
} else { | ||
const schemaTypeField = schemaType.getFields()[fieldName] | ||
if (typeof schemaTypeField !== 'undefined') { | ||
this[kWrapFieldResolver](schemaTypeField, fieldPolicy) | ||
} | ||
} | ||
@@ -137,0 +144,0 @@ } |
@@ -88,3 +88,3 @@ 'use strict' | ||
: {} | ||
for (const [fieldName, fieldPolicy] of Object.entries(typePolicy)) { | ||
for (const [fieldName, fieldPolicies] of Object.entries(typePolicy)) { | ||
// each `fieldName` is a single GraphQL item associated with the directive | ||
@@ -113,8 +113,12 @@ | ||
} | ||
// The null parameters are: https://graphql.org/learn/execution/#root-fields-resolvers | ||
// - parent: it is not possible know it since the resolver is not executed yet | ||
// - args: it is not expected that the introspection query will have arguments for the directives policies | ||
canShowDirectiveField = await policyFunction(fieldPolicy, null, null, context, info) | ||
if (canShowDirectiveField instanceof Error || !canShowDirectiveField) { | ||
canShowDirectiveField = false | ||
for (const fieldPolicy of fieldPolicies) { | ||
// The null parameters are: https://graphql.org/learn/execution/#root-fields-resolvers | ||
// - parent: it is not possible know it since the resolver is not executed yet | ||
// - args: it is not expected that the introspection query will have arguments for the directives policies | ||
canShowDirectiveField = await policyFunction(fieldPolicy, null, null, context, info) | ||
if (canShowDirectiveField instanceof Error || !canShowDirectiveField) { | ||
canShowDirectiveField = false | ||
break | ||
} | ||
} | ||
@@ -121,0 +125,0 @@ } catch (error) { |
{ | ||
"name": "mercurius-auth", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"description": "Mercurius Auth Plugin adds configurable Authentication and Authorization support to Mercurius.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -639,3 +639,3 @@ 'use strict' | ||
test('repeatable directive', async (t) => { | ||
t.plan(4) | ||
t.plan(5) | ||
@@ -663,5 +663,4 @@ const app = Fastify() | ||
applyPolicy: async () => { | ||
t.pass('should be called once') | ||
t.todo('should be called three times but repeatable directives are not supported') | ||
return false | ||
t.pass('should be called three times') | ||
return true | ||
} | ||
@@ -682,3 +681,4 @@ }) | ||
fields: [ | ||
{ name: 'title' } | ||
{ name: 'title' }, | ||
{ name: 'message' } | ||
] | ||
@@ -685,0 +685,0 @@ } |
@@ -12,3 +12,3 @@ 'use strict' | ||
directive @hasRole (role: String!) on OBJECT | FIELD_DEFINITION | ||
directive @hasPermission (grant: String!) on OBJECT | FIELD_DEFINITION | ||
directive @hasPermission (grant: String!) repeatable on OBJECT | FIELD_DEFINITION | ||
@@ -18,2 +18,3 @@ type Message { | ||
message: String @auth | ||
notes: String @hasPermission(grant: "see-all") @hasPermission(grant: "notes") | ||
password: String @hasPermission(grant: "see-all") | ||
@@ -81,2 +82,3 @@ } | ||
message: 'acme one', | ||
notes: 'acme one', | ||
password: 'acme-one' | ||
@@ -87,2 +89,3 @@ }, | ||
message: 'acme two', | ||
notes: 'acme two', | ||
password: 'acme-two' | ||
@@ -308,3 +311,3 @@ } | ||
result (t, responseJson) { | ||
t.plan(3) | ||
t.plan(4) | ||
const { types } = responseJson.data.__schema | ||
@@ -317,3 +320,24 @@ | ||
t.ok(objMessage.fields.find(field => field.name === 'password'), 'role is right') | ||
t.notOk(objMessage.fields.find(field => field.name === 'notes'), 'doesn\'t have the role "notes"') | ||
} | ||
}, | ||
{ | ||
name: 'Introspection query with multiple permissions', | ||
query: getIntrospectionQuery(), | ||
headers: { | ||
'x-token': 'token', | ||
'x-role': 'not-an-admin', | ||
'x-permission': 'see-all,notes' | ||
}, | ||
result (t, responseJson) { | ||
t.plan(4) | ||
const { types } = responseJson.data.__schema | ||
t.notOk(types.find(type => type.name === 'AdminMessage'), 'the AdminMessage type has been filtered') | ||
const objMessage = types.find(type => type.name === 'Message') | ||
t.ok(objMessage, 'the Message type is present') | ||
t.ok(objMessage.fields.find(field => field.name === 'password'), 'role is right') | ||
t.ok(objMessage.fields.find(field => field.name === 'notes'), 'role is right') | ||
} | ||
} | ||
@@ -496,7 +520,11 @@ ].forEach(({ name, query, result, headers }) => { | ||
function hasPermissionContext (context) { | ||
return { permission: context.reply.request.headers['x-permission'] } | ||
const headerValue = context.reply.request.headers['x-permission'] | ||
return { permission: headerValue ? headerValue.split(',') : [] } | ||
} | ||
async function hasPermissionPolicy (authDirectiveAST, parent, args, context, info) { | ||
const needed = authDirectiveAST.arguments.find(arg => arg.name.value === 'grant').value.value | ||
const hasGrant = context.auth.permission === needed | ||
const hasGrant = context.auth.permission.includes(needed) | ||
if (!hasGrant) { | ||
@@ -503,0 +531,0 @@ throw new Error(`Needed ${needed} grant`) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
256801
56
8156
0