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

fix: improve typings on KeycloakContext #12

Merged
merged 2 commits into from
Jul 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/KeycloakContext.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
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
wtrocki marked this conversation as resolved.
Show resolved Hide resolved
}

public hasRole (role: string): boolean {
//@ts-ignore
return this.isAuthenticated() && this.accessToken.hasRole(role)
}
}
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