From 10ad59ef1f270b7044413529e763403419f22862 Mon Sep 17 00:00:00 2001 From: Dara Hayes Date: Wed, 13 May 2020 15:42:27 +0100 Subject: [PATCH 1/3] feat: export isAuthorizedByRole util function --- src/directives/directiveResolvers.ts | 14 +++----------- src/directives/utils.ts | 21 +++++++++++++++++++++ src/index.ts | 1 + 3 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 src/directives/utils.ts diff --git a/src/directives/directiveResolvers.ts b/src/directives/directiveResolvers.ts index 496fa5f..9b17b4a 100644 --- a/src/directives/directiveResolvers.ts +++ b/src/directives/directiveResolvers.ts @@ -1,4 +1,5 @@ import { CONTEXT_KEY } from '../KeycloakContext' +import { isAuthorizedByRole } from './utils' /** * @@ -104,17 +105,8 @@ export const hasRole = (roles: Array) => (next: Function) => (root: any, if (typeof roles === 'string') { roles = [roles] } - - let foundRole = null // this will be the role the user was successfully authorized on - - for (let role of roles) { - if (context[CONTEXT_KEY].hasRole(role)) { - foundRole = role - break - } - } - - if (!foundRole) { + + if (!isAuthorizedByRole(roles, context)) { const error: any = new Error(`User is not authorized. Must have one of the following roles: [${roles}]`); error.code = "FORBIDDEN" throw error diff --git a/src/directives/utils.ts b/src/directives/utils.ts new file mode 100644 index 0000000..ade0d11 --- /dev/null +++ b/src/directives/utils.ts @@ -0,0 +1,21 @@ +import { CONTEXT_KEY } from '../KeycloakContext' + +/** + * + * @param roles the list of roles the user should match against + * @param context the graphql context that contains the user info + */ +export function isAuthorizedByRole(roles: string[], context?: any) { + if (!(context && context[CONTEXT_KEY])) { + console.error('context.kauth is missing. Keycloak integration is probably misconfigured') + return false + } + + for (const role of roles) { + if (context[CONTEXT_KEY].hasRole(role)) { + return true + } + } + + return false +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 25749ce..7f5a08a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from './KeycloakSubscriptionHandler' export * from './KeycloakContext' export * from './directives' export * from './api' +export { isAuthorizedByRole } from './directives/utils' From 4839e630c5bb11760794732d60fc03d1f64d7017 Mon Sep 17 00:00:00 2001 From: Dara Hayes Date: Wed, 13 May 2020 16:39:20 +0100 Subject: [PATCH 2/3] fix: ensure context key is logged in error --- src/directives/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/utils.ts b/src/directives/utils.ts index ade0d11..32fce3e 100644 --- a/src/directives/utils.ts +++ b/src/directives/utils.ts @@ -7,7 +7,7 @@ import { CONTEXT_KEY } from '../KeycloakContext' */ export function isAuthorizedByRole(roles: string[], context?: any) { if (!(context && context[CONTEXT_KEY])) { - console.error('context.kauth is missing. Keycloak integration is probably misconfigured') + console.error(`context.${CONTEXT_KEY} is missing. Keycloak integration is probably misconfigured`) return false } From 843bb771b7f9a6a4dbe9110d15ec17203b779ac8 Mon Sep 17 00:00:00 2001 From: Dara Hayes Date: Wed, 13 May 2020 16:41:56 +0100 Subject: [PATCH 3/3] fix: add test for utils.ts --- test/utils.test.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 test/utils.test.ts diff --git a/test/utils.test.ts b/test/utils.test.ts new file mode 100644 index 0000000..dfa7894 --- /dev/null +++ b/test/utils.test.ts @@ -0,0 +1,48 @@ +import test from 'ava' +import { isAuthorizedByRole } from '../src/directives/utils' +import Keycloak from 'keycloak-connect' +import { KeycloakContextBase } from '../src/KeycloakContext' + +test('isAuthorizedByRole returns the result of token.hasRole', (t) => { + t.plan(4) + const token = { + hasRole: (role: string) => { + t.pass() + return role === 'c' + }, + isExpired: () => { + return false + } + } as Keycloak.Token + + const context = { kauth: new KeycloakContextBase(token) } + + t.truthy(isAuthorizedByRole(['a', 'b', 'c'], context)) +}) + +test('isAuthorizedByRole returns false if hasRole returns false', (t) => { + t.plan(4) + const token = { + hasRole: (role: string) => { + t.pass() + return false + }, + isExpired: () => { + return false + } + } as Keycloak.Token + + const context = { kauth: new KeycloakContextBase(token) } + + t.falsy(isAuthorizedByRole(['a', 'b', 'c'], context)) +}) + +test('isAuthorizedByRole returns false if context is empty', (t) => { + const context = { } + t.falsy(isAuthorizedByRole(['a', 'b', 'c'], context)) +}) + +test('isAuthorizedByRole returns false if context undefined', (t) => { + const context = { } + t.falsy(isAuthorizedByRole(['a', 'b', 'c'], undefined)) +}) \ No newline at end of file