This repository has been archived by the owner on Apr 17, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 23
feat: refactor directives, add auth #8
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
9e330dd
refactor: split hasRole resolver logic from the SchemaDirectiveVisitor
6433511
fix: refactor directives into middleware style approach
c96b112
fix: unit tests for AuthContextProvider
594e049
fix: new test for auth directive, move tests to test folder
39f4ffc
fix: only include src in coverage
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { defaultFieldResolver, GraphQLSchema } from 'graphql' | ||
import { SchemaDirectiveVisitor } from 'graphql-tools' | ||
import { directiveResolvers } from './directiveResolvers' | ||
import { VisitableSchemaType } from 'graphql-tools/dist/schemaVisitor' | ||
|
||
export class AuthDirective extends SchemaDirectiveVisitor { | ||
|
||
constructor (config: { | ||
name: string | ||
visitedType: VisitableSchemaType | ||
schema: GraphQLSchema | ||
context: { [key: string]: any } | ||
}) { | ||
// see https://github.com/apollographql/graphql-tools/issues/837 | ||
super(config as any) | ||
} | ||
|
||
public visitFieldDefinition (field: any) { | ||
const { resolve = defaultFieldResolver } = field | ||
field.resolve = directiveResolvers.auth(resolve) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export const directiveResolvers = { | ||
auth: (next: Function) => (root: any, args: any, context: any, info: any) => { | ||
if (!context.auth || !context.auth.isAuthenticated()) { | ||
throw new Error(`User not Authenticated`) | ||
} | ||
return next(root, args, context, info) | ||
}, | ||
hasRole: (roles: Array<string>) => (next: Function) => (root: any, args: any, context: any, info: any) => { | ||
if (!context.auth || !context.auth.isAuthenticated()) { | ||
throw new Error(`User not Authenticated`) | ||
} | ||
|
||
let foundRole = null // this will be the role the user was successfully authorized on | ||
|
||
foundRole = roles.find((role: string) => { | ||
return context.auth.hasRole(role) | ||
}) | ||
|
||
if (!foundRole) { | ||
throw new Error(`User is not authorized. Must have one of the following roles: [${roles}]`) | ||
} | ||
|
||
return next(root, args, context, info) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
import { HasRoleDirective } from './hasRole' | ||
import { AuthDirective } from './auth' | ||
|
||
export const schemaDirectives = { | ||
auth: AuthDirective, | ||
hasRole: HasRoleDirective | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import test from 'ava' | ||
|
||
import { KeycloakAuthContextProvider } from '../src/AuthContextProvider' | ||
|
||
test('AuthContextProvider accessToken is the access_token in req.kauth', (t) => { | ||
|
||
const req = { | ||
kauth: { | ||
grant: { | ||
access_token: { | ||
hasRole: (role: string) => { | ||
return true | ||
}, | ||
isExpired: () => { | ||
return false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
const provider = new KeycloakAuthContextProvider({ req }) | ||
t.deepEqual(provider.accessToken, req.kauth.grant.access_token) | ||
}) | ||
|
||
test('AuthContextProvider hasRole calls hasRole in the access_token', (t) => { | ||
t.plan(2) | ||
const req = { | ||
kauth: { | ||
grant: { | ||
access_token: { | ||
hasRole: (role: string) => { | ||
t.pass() | ||
return true | ||
}, | ||
isExpired: () => { | ||
return false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
const provider = new KeycloakAuthContextProvider({ req }) | ||
t.truthy(provider.hasRole('')) | ||
}) | ||
|
||
test('AuthContextProvider.isAuthenticated is true when token is defined and isExpired returns false', (t) => { | ||
const req = { | ||
kauth: { | ||
grant: { | ||
access_token: { | ||
hasRole: (role: string) => { | ||
return true | ||
}, | ||
isExpired: () => { | ||
return false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
const provider = new KeycloakAuthContextProvider({ req }) | ||
t.truthy(provider.isAuthenticated()) | ||
}) | ||
|
||
test('AuthContextProvider.isAuthenticated is false when token is defined but isExpired returns true', (t) => { | ||
const req = { | ||
kauth: { | ||
grant: { | ||
access_token: { | ||
hasRole: (role: string) => { | ||
return true | ||
}, | ||
isExpired: () => { | ||
return true | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
const provider = new KeycloakAuthContextProvider({ req }) | ||
t.false(provider.isAuthenticated()) | ||
}) | ||
|
||
test('AuthContextProvider.hasRole is false if token is expired', (t) => { | ||
const req = { | ||
kauth: { | ||
grant: { | ||
access_token: { | ||
hasRole: (role: string) => { | ||
return true | ||
}, | ||
isExpired: () => { | ||
return true | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
const provider = new KeycloakAuthContextProvider({ req }) | ||
t.false(provider.hasRole('')) | ||
}) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this will be vulnerable to code swapping. Using classical for loop may be more secure as Array.find is global.
Request or context aren't so easy to swap.