Skip to content

Commit

Permalink
Merge pull request #59 from Sytten/feature/add-object-shield
Browse files Browse the repository at this point in the history
Add default rule on object
  • Loading branch information
Sytten authored Aug 4, 2020
2 parents a8169ef + a242de3 commit b0e3b25
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 9 deletions.
6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ export {
partial,
} from './builders';
export { ShieldCache } from './rules';
export { nexusShield, FieldShieldResolver } from './plugin';
export {
nexusShield,
FieldShieldResolver,
ObjectTypeShieldResolver,
} from './plugin';
27 changes: 26 additions & 1 deletion src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const FieldShieldType = printedGenTyping({
optional: true,
name: 'shield',
description: `
Rule to execute
Authorization rule to execute for this field
`,
type: 'FieldShieldResolver<TypeName, FieldName>',
imports: [FieldShieldImport],
Expand All @@ -30,6 +30,26 @@ export type FieldShieldResolver<
FieldName extends string
> = ShieldRule<TypeName, FieldName>;

const ObjectTypeShieldImport = printedGenTypingImport({
module: 'nexus-shield',
bindings: ['ObjectTypeShieldResolver'],
});

const ObjectTypeFieldShieldType = printedGenTyping({
optional: true,
name: 'shield',
description: `
Default authorization rule to execute on all fields of this object
`,
type: 'ObjectTypeShieldResolver<TypeName>',
imports: [ObjectTypeShieldImport],
});

export type ObjectTypeShieldResolver<TypeName extends string> = ShieldRule<
TypeName,
never
>;

export const nexusShield = (settings: ShieldPluginSettings) => {
const options = {
defaultRule: settings.defaultRule || allow,
Expand All @@ -41,13 +61,18 @@ export const nexusShield = (settings: ShieldPluginSettings) => {
name: 'Nexus Shield Plugin',
description: 'Ease the creation of the authorization layer',
fieldDefTypes: FieldShieldType,
objectTypeDefTypes: ObjectTypeFieldShieldType,
onCreateFieldResolver(config) {
// Find the field rule
const objectRule =
config.parentTypeConfig.extensions?.nexus?.config.shield;
const fieldRule = config.fieldConfig.extensions?.nexus?.config.shield;

let rule: ShieldRule<any, any> | undefined;
if (isShieldRule(fieldRule)) {
rule = fieldRule;
} else if (isShieldRule(objectRule)) {
rule = objectRule;
} else if (options.defaultRule) {
rule = options.defaultRule;
}
Expand Down
9 changes: 7 additions & 2 deletions tests/fixtures/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import { ruleType } from '../../src';

export const Test = objectType({
name: 'Test',
shield: ruleType({
resolve(_root, _args, _ctx) {
throw new AuthenticationError('OBJECT');
},
}),
definition(t) {
t.id('id');
t.string('publicProp', {
shield: ruleType({
resolve(_root, _args, _ctx) {
Expand All @@ -28,6 +32,7 @@ export const Test = objectType({
},
}),
});
t.string('defaultProp');
},
});

Expand All @@ -38,10 +43,10 @@ export const QueryTest = extendType({
type: Test,
resolve(_root, _args, _ctx) {
return {
id: 'BEEF',
publicProp: 'public',
privateProp: 'private',
throwProp: 'throwProp',
defaultProp: 'defaultProp',
};
},
});
Expand Down
11 changes: 10 additions & 1 deletion tests/fixtures/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { makeSchema } from '@nexus/schema';
import { ApolloServer, ForbiddenError } from 'apollo-server';
import * as path from 'path';

import { allow, FieldShieldResolver, nexusShield } from '../../src';
import {
allow,
FieldShieldResolver,
nexusShield,
ObjectTypeShieldResolver,
} from '../../src';
import * as types from './schema';

declare global {
Expand All @@ -12,6 +17,10 @@ declare global {
> {
shield?: FieldShieldResolver<TypeName, FieldName>;
}

interface NexusGenPluginTypeConfig<TypeName extends string> {
shield?: ObjectTypeShieldResolver<TypeName>;
}
}

const schema = makeSchema({
Expand Down
19 changes: 15 additions & 4 deletions tests/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,20 @@ describe('Integration tests', () => {
const query = gql`
query {
test {
id
publicProp
}
}
`;

const result = await client.query({ query });

expect(result.data).toEqual({ test: { id: 'BEEF', publicProp: 'public' } });
expect(result.data).toEqual({ test: { publicProp: 'public' } });
});

test('Server should return default error if not authorized', async () => {
const query = gql`
query {
test {
id
privateProp
}
}
Expand All @@ -47,7 +45,6 @@ describe('Integration tests', () => {
const query = gql`
query {
test {
id
throwProp
}
}
Expand All @@ -57,4 +54,18 @@ describe('Integration tests', () => {

expect(result.errors[0].message).toEqual('CUSTOM');
});

test('Server should use default object rule', async () => {
const query = gql`
query {
test {
defaultProp
}
}
`;

const result = await client.query({ query });

expect(result.errors[0].message).toEqual('OBJECT');
});
});

0 comments on commit b0e3b25

Please sign in to comment.