Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
fix: improve typings on KeycloakContext
Browse files Browse the repository at this point in the history
  • Loading branch information
Dara Hayes committed Jul 10, 2019
1 parent 77f9e99 commit 18c295e
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 33 deletions.
13 changes: 6 additions & 7 deletions src/KeycloakContext.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { AuthContextProvider } from './api'
import Keycloak from 'keycloak-connect'

export class KeycloakContext implements AuthContextProvider {
public readonly request: any
public readonly accessToken: any
public readonly authenticated: boolean
public readonly request: Keycloak.GrantedRequest
public readonly accessToken: Keycloak.Token | undefined

constructor ({ req }: { req: any }) {
constructor ({ req }: { req: Keycloak.GrantedRequest }) {
this.request = req
this.accessToken = (req && req.kauth && req.kauth.grant) ? req.kauth.grant.access_token : undefined
this.authenticated = this.accessToken && !this.accessToken.isExpired()
}

public isAuthenticated (): boolean {
return this.authenticated
return (this.accessToken && !this.accessToken.isExpired()) ? true : false
}

public hasRole (role: string): boolean {
return this.isAuthenticated() && this.accessToken.hasRole(role)
return ((this.accessToken && !this.accessToken.isExpired()) && this.accessToken.hasRole(role)) ? true : false
}
}
14 changes: 8 additions & 6 deletions test/AuthContextProvider.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import test from 'ava'
import Keycloak from 'keycloak-connect'

import { KeycloakContext } from '../src/KeycloakContext'

Expand All @@ -17,10 +18,11 @@ test('AuthContextProvider accessToken is the access_token in req.kauth', (t) =>
}
}
}
}
} as Keycloak.GrantedRequest

const provider = new KeycloakContext({ req })
t.deepEqual(provider.accessToken, req.kauth.grant.access_token)
const token = req.kauth.grant && req.kauth.grant.access_token ? req.kauth.grant.access_token : undefined
t.deepEqual(provider.accessToken, token)
})

test('AuthContextProvider hasRole calls hasRole in the access_token', (t) => {
Expand All @@ -39,7 +41,7 @@ test('AuthContextProvider hasRole calls hasRole in the access_token', (t) => {
}
}
}
}
} as Keycloak.GrantedRequest

const provider = new KeycloakContext({ req })
t.truthy(provider.hasRole(''))
Expand All @@ -59,7 +61,7 @@ test('AuthContextProvider.isAuthenticated is true when token is defined and isEx
}
}
}
}
} as Keycloak.GrantedRequest

const provider = new KeycloakContext({ req })
t.truthy(provider.isAuthenticated())
Expand All @@ -79,7 +81,7 @@ test('AuthContextProvider.isAuthenticated is false when token is defined but isE
}
}
}
}
} as Keycloak.GrantedRequest

const provider = new KeycloakContext({ req })
t.false(provider.isAuthenticated())
Expand All @@ -99,7 +101,7 @@ test('AuthContextProvider.hasRole is false if token is expired', (t) => {
}
}
}
}
} as Keycloak.GrantedRequest

const provider = new KeycloakContext({ req })
t.false(provider.hasRole(''))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ test('onSubscriptionConnect throws if no connectionParams Provided', async t =>
}
} as unknown as Keycloak.Keycloak

const securityService = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })

await t.throwsAsync(async () => {
await securityService.onSubscriptionConnect(null, {}, {})
await subscriptionHandler.onSubscriptionConnect(null, {}, {})
}, 'Access Denied - missing connection parameters for Authentication')
})

Expand All @@ -33,11 +33,11 @@ test('onSubscriptionConnect throws if no connectionParams is not an object', asy
}
} as unknown as Keycloak.Keycloak

const securityService = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const connectionParams = 'not an object'

await t.throwsAsync(async () => {
await securityService.onSubscriptionConnect(connectionParams, {}, {})
await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
}, 'Access Denied - missing connection parameters for Authentication')
})

Expand All @@ -52,11 +52,11 @@ test('onSubscriptionConnect throws if no Auth provided', async t => {
}
} as unknown as Keycloak.Keycloak

const securityService = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const connectionParams = { Authorization: undefined }

await t.throwsAsync(async () => {
await securityService.onSubscriptionConnect(connectionParams, {}, {})
await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
}, 'Access Denied - missing Authorization field in connection parameters')
})

Expand All @@ -73,10 +73,10 @@ test('onSubscriptionConnect returns a token Object if the keycloak library consi

const tokenString = 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfa29BTUtBcW1xQjcxazNGeDdBQ0xvNXZqMXNoWVZwSkdJM2FScFl4allZIn0.eyJqdGkiOiJjN2UyMzA0NS00NGVmLTQ1ZDItOGY0Yy1jODA4OTlhYzljYzIiLCJleHAiOjE1NTc5NjcxMjQsIm5iZiI6MCwiaWF0IjoxNTU3OTMxMTI0LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvdm95YWdlci10ZXN0aW5nIiwiYXVkIjoidm95YWdlci10ZXN0aW5nIiwic3ViIjoiM2Y4MDRiNWEtM2U3Ni00YzI2LTk4ZTYtNDU1ZDNlMzUzZmY3IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidm95YWdlci10ZXN0aW5nIiwiYXV0aF90aW1lIjoxNTU3OTMxMTI0LCJzZXNzaW9uX3N0YXRlIjoiOThiNTM2ODAtODU5MC00MzFmLWFiNzctMDY0MDFmODgzYTY5IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInByZWZlcnJlZF91c2VybmFtZSI6ImRldmVsb3BlciJ9.iF3WdY6hwlZIX2bq40fs0GhxG991TqtBEuKbX7A8DMfgOj2QFDyNHGLVzEiJqMal44pmhlWhtOSoVp77ZZ57HdatEYqYaTnc8C8ajA8A1yxOX81D0lFu2jmC3WpKS2H0prrjdPPZyf82YpbYuwYAyiKJMpJSiRC2fGk1Owsg9O6CSj8cFbKfrS4msE1Y90S84qwrDfRYFSFFdsmeTvC71qyj4ZhNqNfPWbIwymlnYJ6xYbmTrZBv2GktXBLd0BnSu5QFoHgjiCxG3cyFV4tCIBpvWjebI6rCUehD6TTIXiW4uVOp9YPWvyZH8WznFdtq36CDb51abWJ8EUquog7M1w'

const securityService = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const connectionParams = { Authorization: tokenString }

const token = await securityService.onSubscriptionConnect(connectionParams, {}, {})
const token = await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
t.truthy(token instanceof Token)
})

Expand All @@ -95,10 +95,10 @@ test('the token object will have hasRole, hasRealmRole and hasPermissions if the
// works with a clientId called 'voyager-testing' and has a client role 'tester'
const tokenString = 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfa29BTUtBcW1xQjcxazNGeDdBQ0xvNXZqMXNoWVZwSkdJM2FScFl4allZIn0.eyJqdGkiOiJmMWZjZDdmNS1mMWM0LTQyYWQtYjFmOC00ZWVhNzNiZWU2N2MiLCJleHAiOjE1NTc5Njc4MzksIm5iZiI6MCwiaWF0IjoxNTU3OTMxODM5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvdm95YWdlci10ZXN0aW5nIiwiYXVkIjoidm95YWdlci10ZXN0aW5nIiwic3ViIjoiM2Y4MDRiNWEtM2U3Ni00YzI2LTk4ZTYtNDU1ZDNlMzUzZmY3IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidm95YWdlci10ZXN0aW5nIiwiYXV0aF90aW1lIjoxNTU3OTMxODM5LCJzZXNzaW9uX3N0YXRlIjoiMDQ2YTk4N2QtNmI4NS00Njk5LTllNmUtNGIyYmVlYzBhYzNhIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7InZveWFnZXItdGVzdGluZyI6eyJyb2xlcyI6WyJ0ZXN0ZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInByZWZlcnJlZF91c2VybmFtZSI6ImRldmVsb3BlciJ9.YjmImGZbs5-s0K1KEYnIedW3peIUz4rORoOUTNFgE2sEKHe2hvvDg48NNybVsJDZc29Al-6OiUw8En5GpschqHHb79GqStEtuJ5T2UZb5sC2B7sX1jAvZAafkxCcOMajEbgS5qVPGoFhDTTej06sGfQwI8h0Igwle86O8IDMbEK-uN_oVa1xKTrFtvsFKekS3Yz3_qSVlmAhOKyYejEg8hkZOvJzHXK9_zsi3Ze6MLq2VCSJE-13UnZuSvdD36FydJQXkZ7elKYqj_HcyPIMAkBuKPhYAXZ9laMo2X4wM6gSIFZXKPeG44eUAGH7estqeG2oXNsdbPaixoNFHHuMqA'

const securityService = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const connectionParams = { Authorization: tokenString, clientId: 'voyager-testing' }

const token: Token = await securityService.onSubscriptionConnect(connectionParams, {}, {})
const token: Token = await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
t.truthy(token instanceof Token)
t.truthy(token.hasRole('tester'))
})
Expand All @@ -119,10 +119,10 @@ test('If the keycloak token validation fails, then onSubscriptionConnect will th
// works with a clientId called 'voyager-testing' and has a client role 'tester'
const tokenString = 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfa29BTUtBcW1xQjcxazNGeDdBQ0xvNXZqMXNoWVZwSkdJM2FScFl4allZIn0.eyJqdGkiOiJmMWZjZDdmNS1mMWM0LTQyYWQtYjFmOC00ZWVhNzNiZWU2N2MiLCJleHAiOjE1NTc5Njc4MzksIm5iZiI6MCwiaWF0IjoxNTU3OTMxODM5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvdm95YWdlci10ZXN0aW5nIiwiYXVkIjoidm95YWdlci10ZXN0aW5nIiwic3ViIjoiM2Y4MDRiNWEtM2U3Ni00YzI2LTk4ZTYtNDU1ZDNlMzUzZmY3IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidm95YWdlci10ZXN0aW5nIiwiYXV0aF90aW1lIjoxNTU3OTMxODM5LCJzZXNzaW9uX3N0YXRlIjoiMDQ2YTk4N2QtNmI4NS00Njk5LTllNmUtNGIyYmVlYzBhYzNhIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7InZveWFnZXItdGVzdGluZyI6eyJyb2xlcyI6WyJ0ZXN0ZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInByZWZlcnJlZF91c2VybmFtZSI6ImRldmVsb3BlciJ9.YjmImGZbs5-s0K1KEYnIedW3peIUz4rORoOUTNFgE2sEKHe2hvvDg48NNybVsJDZc29Al-6OiUw8En5GpschqHHb79GqStEtuJ5T2UZb5sC2B7sX1jAvZAafkxCcOMajEbgS5qVPGoFhDTTej06sGfQwI8h0Igwle86O8IDMbEK-uN_oVa1xKTrFtvsFKekS3Yz3_qSVlmAhOKyYejEg8hkZOvJzHXK9_zsi3Ze6MLq2VCSJE-13UnZuSvdD36FydJQXkZ7elKYqj_HcyPIMAkBuKPhYAXZ9laMo2X4wM6gSIFZXKPeG44eUAGH7estqeG2oXNsdbPaixoNFHHuMqA'

const securityService = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const connectionParams = { Authorization: tokenString, clientId: 'voyager-testing' }

await t.throwsAsync(async () => {
await securityService.onSubscriptionConnect(connectionParams, {}, {})
await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
}, `Access Denied - ${new Error(errorMsg)}`)
})
9 changes: 6 additions & 3 deletions test/auth.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import test from 'ava'
import sinon from 'sinon'

import Keycloak from 'keycloak-connect'
import { GraphQLSchema } from 'graphql'
import { VisitableSchemaType } from 'graphql-tools/dist/schemaVisitor'
import { AuthDirective } from '../src/directives/schemaDirectiveVisitors'
Expand Down Expand Up @@ -42,7 +43,8 @@ test('happy path: context.kauth.isAuthenticated() is called, then original resol
}
}
}
}
} as Keycloak.GrantedRequest

const context = {
request: req,
kauth: new KeycloakContext({ req })
Expand Down Expand Up @@ -86,7 +88,8 @@ test('resolver will throw if context.kauth is not present', async (t) => {
}
}
}
}
} as Keycloak.GrantedRequest

const context = {
request: req
}
Expand Down Expand Up @@ -116,7 +119,7 @@ test('resolver will throw if context.kauth present but context.kauth.isAuthentic

const root = {}
const args = {}
const req = {}
const req = {} as Keycloak.GrantedRequest

const context = {
request: req,
Expand Down
15 changes: 10 additions & 5 deletions test/hasRole.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import test from 'ava'

import Keycloak from 'keycloak-connect'
import { GraphQLSchema } from 'graphql'
import { VisitableSchemaType } from 'graphql-tools/dist/schemaVisitor'
import { HasRoleDirective } from '../src/directives/schemaDirectiveVisitors'
Expand Down Expand Up @@ -50,7 +51,8 @@ test('context.auth.hasRole() is called', async (t) => {
}
}
}
}
} as Keycloak.GrantedRequest

const context = {
request: req,
kauth: new KeycloakContext({ req })
Expand Down Expand Up @@ -99,7 +101,8 @@ test('visitFieldDefinition accepts an array of roles', async (t) => {
}
}
}
}
} as Keycloak.GrantedRequest

const context = {
request: req,
kauth: new KeycloakContext({ req })
Expand Down Expand Up @@ -135,7 +138,7 @@ test('if there is no authentication, then an error is returned and the original

const root = {}
const args = {}
const req = {}
const req = {} as Keycloak.GrantedRequest
const context = {
request: req,
kauth: new KeycloakContext({ req })
Expand Down Expand Up @@ -186,7 +189,8 @@ test('if token does not have the required role, then an error is returned and th
}
}
}
}
} as Keycloak.GrantedRequest

const context = {
request: req,
kauth: new KeycloakContext({ req })
Expand Down Expand Up @@ -261,7 +265,8 @@ test('context.auth.hasRole() works even if request is not supplied in context',
}
}
}
}
} as Keycloak.GrantedRequest

const context = {
kauth: new KeycloakContext({ req })
}
Expand Down

0 comments on commit 18c295e

Please sign in to comment.