Skip to content

Commit

Permalink
fix tests, compare access keys explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
njlie committed Jul 25, 2024
1 parent fe8175b commit 345cd5e
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 32 deletions.
48 changes: 29 additions & 19 deletions packages/auth/src/access/utils.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { isDeepStrictEqual } from 'util'
import { AccessItem, AccessAction } from '@interledger/open-payments'

type OutgoingPaymentAccess = Extract<AccessItem, { type: 'outgoing-payment' }>
type OutgoingPaymentOrIncomingPaymentAccess = Exclude<
AccessItem,
{ type: 'quote' }
>

export function compareRequestAndGrantAccessItems(
requestAccessItem: AccessItem,
grantAccessItem: AccessItem
): boolean {
const { actions: requestAccessItemActions, ...restOfRequestAccessItem } =
requestAccessItem
const { actions: grantAccessItemActions, ...restOfgrantAccessItem } =
const { actions: grantAccessItemActions, ...restOfGrantAccessItem } =
grantAccessItem

// Validate action arrays
for (const actionItem of requestAccessItemActions) {
if (
!grantAccessItemActions.find(
Expand All @@ -24,24 +31,27 @@ export function compareRequestAndGrantAccessItems(
return false
}

Object.keys(restOfRequestAccessItem).forEach((key) => {
const requestAccessItemValue =
restOfRequestAccessItem[key as keyof typeof restOfRequestAccessItem]
if (
typeof requestAccessItemValue === 'object' &&
!isDeepStrictEqual(
requestAccessItemValue,
restOfgrantAccessItem[key as keyof typeof restOfgrantAccessItem]
)
) {
return false
} else if (
requestAccessItemValue !==
restOfgrantAccessItem[key as keyof typeof restOfgrantAccessItem]
) {
return false
}
})
// Validate limits object, if included
if (
(restOfRequestAccessItem as OutgoingPaymentAccess).limits &&
!isDeepStrictEqual(
(restOfRequestAccessItem as OutgoingPaymentAccess).limits,
(restOfGrantAccessItem as OutgoingPaymentAccess).limits
)
) {
return false
}

// Validate remaining keys
if (
restOfRequestAccessItem.type !== restOfGrantAccessItem.type ||
(restOfRequestAccessItem as OutgoingPaymentOrIncomingPaymentAccess)
.identifier !==
(restOfGrantAccessItem as OutgoingPaymentOrIncomingPaymentAccess)
.identifier
) {
return false
}

return true
}
55 changes: 47 additions & 8 deletions packages/auth/src/accessToken/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,7 @@ describe('Access Token Routes', (): void => {
expect(ctx.body).toEqual({
active: true,
grant: grant.id,
access: [
{
type: access.type,
actions: access.actions,
limits: access.limits,
identifier: access.identifier
}
],
access: [],
client: CLIENT
})
})
Expand Down Expand Up @@ -287,6 +280,52 @@ describe('Access Token Routes', (): void => {
})
})

test('Only returns requested access during successful introspection', async (): Promise<void> => {
const secondAccess: AccessItem = {
type: 'quote',
actions: ['create', 'read']
}
await Access.query(trx).insertAndFetch({
grantId: grant.id,
...secondAccess
})

const ctx = createContext<IntrospectContext>(
{
headers: {
Accept: 'application/json'
},
url: '/',
method: 'POST'
},
{}
)

ctx.request.body = {
access_token: token.value,
access: [secondAccess as AccessItem]
}

await expect(accessTokenRoutes.introspect(ctx)).resolves.toBeUndefined()
expect(ctx.response).toSatisfyApiSpec()
expect(ctx.status).toBe(200)
expect(ctx.response.get('Content-Type')).toBe(
'application/json; charset=utf-8'
)

expect(ctx.body).toEqual({
active: true,
grant: grant.id,
access: [
{
type: secondAccess.type,
actions: secondAccess.actions
}
],
client: CLIENT
})
})

test('Cannot introspect token with incorrect access', async (): Promise<void> => {
const ctx = createContext<IntrospectContext>(
{
Expand Down
44 changes: 41 additions & 3 deletions packages/auth/src/accessToken/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe('Access Token Service', (): void => {
test('Can introspect active token', async (): Promise<void> => {
await expect(
accessTokenService.introspect(accessToken.value)
).resolves.toEqual({ grant })
).resolves.toEqual({ grant, access: [] })
})

test('Can introspect expired token', async (): Promise<void> => {
Expand Down Expand Up @@ -171,7 +171,7 @@ describe('Access Token Service', (): void => {
accessTokenService.introspect(accessToken.value, [
outgoingPaymentAccess
])
).resolves.toEqual({ grant, accessItem: grant.access[0] })
).resolves.toEqual({ grant, access: [grant.access[0]] })
})

test('Can introspect active token with partial access actions', async (): Promise<void> => {
Expand All @@ -181,7 +181,45 @@ describe('Access Token Service', (): void => {
}
await expect(
accessTokenService.introspect(accessToken.value, [access])
).resolves.toEqual({ grant, accessItem: grant.access[0] })
).resolves.toEqual({ grant, access: [grant.access[0]] })
})

test('Introspection only returns requested access', async (): Promise<void> => {
const grantWithTwoAccesses = await Grant.query(trx).insertAndFetch(
generateBaseGrant({ state: GrantState.Approved })
)
grantWithTwoAccesses.access = [
await Access.query(trx).insertAndFetch({
grantId: grantWithTwoAccesses.id,
...BASE_ACCESS
})
]
const secondAccessItem: AccessItem = {
type: 'quote',
actions: ['create', 'read']
}
const dbSecondAccess = await Access.query(trx).insertAndFetch({
grantId: grantWithTwoAccesses.id,
...secondAccessItem
})

grantWithTwoAccesses.access.push(dbSecondAccess)

const accessTokenForTwoAccessGrant = await AccessToken.query(trx).insert({
value: 'test-access-token-two-access',
managementId: v4(),
grantId: grantWithTwoAccesses.id,
expiresIn: 1234
})

await expect(
accessTokenService.introspect(accessTokenForTwoAccessGrant.value, [
secondAccessItem
])
).resolves.toEqual({
grant: grantWithTwoAccesses,
access: [dbSecondAccess]
})
})

test('Cannot introspect non-existing token', async (): Promise<void> => {
Expand Down
3 changes: 1 addition & 2 deletions packages/auth/src/accessToken/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ async function introspect(
.findOne({ value: tokenValue })
.withGraphFetched('grant.access')

let foundAccessItem: Access | undefined
const foundAccess: Access[] = []
if (!token) return
if (isTokenExpired(token)) {
Expand All @@ -94,7 +93,7 @@ async function introspect(
if (access) {
for (const accessItem of access) {
const { access: grantAccess } = token.grant
foundAccessItem = grantAccess.find((grantAccessItem) =>
const foundAccessItem = grantAccess.find((grantAccessItem) =>
compareRequestAndGrantAccessItems(
accessItem,
toOpenPaymentsAccess(grantAccessItem)
Expand Down

0 comments on commit 345cd5e

Please sign in to comment.