Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(backend): replace GrantReference with OutgoingPaymentGrant #800

Merged
merged 2 commits into from
Dec 1, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ exports.up = function (knex) {
table.uuid('assetId').notNullable()
table.foreign('assetId').references('assets.id')

table.string('grantId').nullable()
table.foreign('grantId').references('grantReferences.id')
table.string('clientId').nullable()

table.timestamp('createdAt').defaultTo(knex.fn.now())
table.timestamp('updatedAt').defaultTo(knex.fn.now())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ exports.up = function (knex) {
table.string('externalRef').nullable()
table.uuid('connectionId').nullable()

table.string('grantId').nullable()
table.foreign('grantId').references('grantReferences.id')
table.string('clientId').nullable()

table.uuid('assetId').notNullable()
table.foreign('assetId').references('assets.id')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
exports.up = function (knex) {
return knex.schema.createTable('outgoingPaymentGrants', function (table) {
table.string('id').notNullable().primary()
})
}

exports.down = function (knex) {
return knex.schema.dropTableIfExists('outgoingPaymentGrants')
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ exports.up = function (knex) {
table.string('description').nullable()
table.string('externalRef').nullable()

table.string('clientId').nullable()

table.string('grantId').nullable()
table.foreign('grantId').references('grantReferences.id')
table.foreign('grantId').references('outgoingPaymentGrants.id')

// Open payments payment pointer corresponding to wallet account
// from which to request funds for payment
Expand Down
2 changes: 0 additions & 2 deletions packages/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import { addDirectivesToSchema } from './graphql/directives'
import { Session } from './session/util'
import { createValidatorMiddleware, HttpMethod, isHttpMethod } from 'openapi'
import { PaymentPointerKeyService } from './paymentPointerKey/service'
import { GrantReferenceService } from './open_payments/grantReference/service'
import { AuthenticatedClient } from 'open-payments'

export interface AppContextData {
Expand Down Expand Up @@ -147,7 +146,6 @@ export interface AppServices {
apiKeyService: Promise<ApiKeyService>
sessionService: Promise<SessionService>
paymentPointerKeyService: Promise<PaymentPointerKeyService>
grantReferenceService: Promise<GrantReferenceService>
openPaymentsClient: Promise<AuthenticatedClient>
}

Expand Down
10 changes: 0 additions & 10 deletions packages/backend/src/graphql/resolvers/incoming_payment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { createIncomingPayment } from '../../tests/incomingPayment'
import { createPaymentPointer } from '../../tests/paymentPointer'
import { truncateTables } from '../../tests/tableManager'
import { v4 as uuid } from 'uuid'
import { GrantReference } from '../../open_payments/grantReference/model'
import { GrantReferenceService } from '../../open_payments/grantReference/service'
import { IncomingPaymentService } from '../../open_payments/payment/incoming/service'
import {
IncomingPaymentResponse,
Expand All @@ -30,8 +28,6 @@ describe('Incoming Payment Resolver', (): void => {
let appContainer: TestContainer
let knex: Knex
let paymentPointerId: string
let grantReferenceService: GrantReferenceService
let grantRef: GrantReference
let incomingPaymentService: IncomingPaymentService

const asset = randomAsset()
Expand All @@ -40,7 +36,6 @@ describe('Incoming Payment Resolver', (): void => {
deps = await initIocContainer(Config)
appContainer = await createTestApp(deps)
knex = await deps.use('knex')
grantReferenceService = await deps.use('grantReferenceService')
incomingPaymentService = await deps.use('incomingPaymentService')
})

Expand All @@ -53,18 +48,13 @@ describe('Incoming Payment Resolver', (): void => {
describe('Payment pointer incoming payments', (): void => {
beforeEach(async (): Promise<void> => {
paymentPointerId = (await createPaymentPointer(deps, { asset })).id
grantRef = await grantReferenceService.create({
id: uuid(),
clientId: uuid()
})
})

getPageTests({
getClient: () => appContainer.apolloClient,
createModel: () =>
createIncomingPayment(deps, {
paymentPointerId,
grantId: grantRef.id,
incomingAmount: {
value: BigInt(123),
assetCode: asset.code,
Expand Down
5 changes: 0 additions & 5 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import { createAuthenticatedClient as createOpenPaymentsClient } from 'open-paym
import { createConnectionService } from './open_payments/connection/service'
import { createConnectionRoutes } from './open_payments/connection/routes'
import { createPaymentPointerKeyService } from './paymentPointerKey/service'
import { createGrantReferenceService } from './open_payments/grantReference/service'
import { createReceiverService } from './open_payments/receiver/service'

BigInt.prototype.toJSON = function () {
Expand Down Expand Up @@ -309,16 +308,12 @@ export function initIocContainer(
quoteService: await deps.use('quoteService')
})
})
container.singleton('grantReferenceService', async () => {
return createGrantReferenceService()
})
container.singleton('outgoingPaymentService', async (deps) => {
return await createOutgoingPaymentService({
logger: await deps.use('logger'),
knex: await deps.use('knex'),
accountingService: await deps.use('accountingService'),
receiverService: await deps.use('receiverService'),
grantReferenceService: await deps.use('grantReferenceService'),
makeIlpPlugin: await deps.use('makeIlpPlugin'),
peerService: await deps.use('peerService')
})
Expand Down
127 changes: 1 addition & 126 deletions packages/backend/src/open_payments/auth/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import { HttpMethod, RequestValidator } from 'openapi'
import { createTestApp, TestContainer } from '../../tests/app'
import { createPaymentPointer } from '../../tests/paymentPointer'
import { truncateTables } from '../../tests/tableManager'
import { GrantReference } from '../grantReference/model'
import { GrantReferenceService } from '../grantReference/service'
import { setup, SetupOptions } from '../payment_pointer/model.test'
import { HttpSigContext, JWKWithRequired, KeyInfo } from 'auth'
import { generateTestKeys, generateSigHeaders } from 'auth/src/tests/signature'
Expand All @@ -39,7 +37,6 @@ describe('Auth Middleware', (): void => {
let ctx: HttpSigContext
let next: jest.MockedFunction<() => Promise<void>>
let validateRequest: RequestValidator<IntrospectionBody>
let grantReferenceService: GrantReferenceService
let mockKeyInfo: KeyInfo
const token = 'OS9M2PMHKUR64TB8N6BW7OZB8CDFONP219RP1LT0'
let generatedKeyPair: {
Expand Down Expand Up @@ -132,7 +129,6 @@ describe('Auth Middleware', (): void => {
path: requestPath,
method: HttpMethod.POST
})
grantReferenceService = await deps.use('grantReferenceService')
generatedKeyPair = await generateTestKeys()
requestMethod = HttpMethod.POST.toUpperCase() as RequestMethod
requestBody = {
Expand Down Expand Up @@ -235,33 +231,6 @@ describe('Auth Middleware', (): void => {
expect(next).not.toHaveBeenCalled()
scope.done()
})
test('returns 500 for not matching clientId', async (): Promise<void> => {
const grant = new TokenInfo(
{
active: true,
clientId: uuid(),
grant: uuid(),
access: [
{
type: AccessType.IncomingPayment,
actions: [AccessAction.Read],
identifier: ctx.paymentPointer.url
}
]
},
mockKeyInfo
)
await grantReferenceService.create({
id: grant.grant,
clientId: uuid()
})
const scope = mockAuthServer(grant.toJSON())
await expect(middleware(ctx, next)).rejects.toMatchObject({
status: 500
})
expect(next).not.toHaveBeenCalled()
scope.done()
})

test.each`
limitAccount
Expand Down Expand Up @@ -306,80 +275,13 @@ describe('Auth Middleware', (): void => {
mockKeyInfo
)
const scope = mockAuthServer(grant.toJSON())
const next = jest.fn().mockImplementation(async () => {
await expect(
GrantReference.query().findById(grant.grant)
).resolves.toBeUndefined()
})
const next = jest.fn()
await expect(middleware(ctx, next)).resolves.toBeUndefined()
expect(next).toHaveBeenCalled()
expect(ctx.grant).toEqual(grant)
scope.done()
}
)
const types = {
[AccessType.IncomingPayment]: Object.values(AccessAction),
[AccessType.OutgoingPayment]: Object.values(AccessAction).filter(
(action) => action !== AccessAction.Complete
),
[AccessType.Quote]: [
AccessAction.Create,
AccessAction.Read
// TODO
// AccessAction.ReadAll
]
}
Object.values(AccessType).forEach((type) => {
for (const action of types[type]) {
const description =
action === AccessAction.Create
? 'stores grant details'
: 'does not store grant details'
test(`${description} for ${type} on the ${action} path`, async (): Promise<void> => {
const actionPathMiddleware: AppMiddleware = createAuthMiddleware({
type: type,
action: action
})
const grant = new TokenInfo(
{
active: true,
clientId: uuid(),
grant: uuid(),
access: [
{
type: type,
actions: [action],
identifier: ctx.paymentPointer.url
}
]
},
mockKeyInfo
)
const scope = mockAuthServer(grant.toJSON())
let next
if (action === AccessAction.Create) {
next = jest.fn().mockImplementation(async () => {
await expect(
GrantReference.query().findById(grant.grant)
).resolves.toEqual({
id: grant.grant,
clientId: grant.clientId
})
})
} else {
next = jest.fn().mockImplementation(async () => {
await expect(
GrantReference.query().findById(grant.grant)
).resolves.toBeUndefined()
})
}
await expect(actionPathMiddleware(ctx, next)).resolves.toBeUndefined()
expect(next).toHaveBeenCalled()
expect(ctx.grant).toEqual(grant)
scope.isDone()
})
}
})

test('bypasses token introspection for configured DEV_ACCESS_TOKEN', async (): Promise<void> => {
ctx.headers.authorization = `GNAP ${Config.devAccessToken}`
Expand All @@ -390,33 +292,6 @@ describe('Auth Middleware', (): void => {
expect(next).toHaveBeenCalled()
})

test('sets the context and calls next if grant has been seen before', async (): Promise<void> => {
const grant = new TokenInfo(
{
active: true,
clientId: uuid(),
grant: uuid(),
access: [
{
type: AccessType.IncomingPayment,
actions: [AccessAction.Read],
identifier: ctx.paymentPointer.url
}
]
},
mockKeyInfo
)
await grantReferenceService.create({
id: grant.grant,
clientId: grant.clientId
})
const scope = mockAuthServer(grant.toJSON())
await expect(middleware(ctx, next)).resolves.toBeUndefined()
expect(next).toHaveBeenCalled()
expect(ctx.grant).toEqual(grant)
scope.done()
})

test('returns 200 with valid http signature without body', async (): Promise<void> => {
await prepareTest(false)
const grant = new TokenInfo(
Expand Down
26 changes: 0 additions & 26 deletions packages/backend/src/open_payments/auth/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { AccessType, AccessAction } from './grant'
import { Transaction } from 'objection'
import { GrantReference } from '../grantReference/model'
import { HttpSigContext, verifySigAndChallenge } from 'auth'

export function createAuthMiddleware({
Expand All @@ -15,10 +13,6 @@ export function createAuthMiddleware({
next: () => Promise<unknown>
): Promise<void> => {
const config = await ctx.container.use('config')
const grantReferenceService = await ctx.container.use(
'grantReferenceService'
)
const logger = await ctx.container.use('logger')
try {
const parts = ctx.request.headers.authorization?.split(' ')
if (parts?.length !== 2 || parts[0] !== 'GNAP') {
Expand Down Expand Up @@ -55,26 +49,6 @@ export function createAuthMiddleware({
ctx.throw(401, `Invalid signature`)
}
}
await GrantReference.transaction(async (trx: Transaction) => {
const grantRef = await grantReferenceService.get(grant.grant, trx)
if (grantRef) {
if (grantRef.clientId !== grant.clientId) {
logger.debug(
`clientID ${grant.clientId} for grant ${grant.grant} does not match internal reference clientId ${grantRef.clientId}.`
)
ctx.throw(500)
}
} else if (action === AccessAction.Create) {
// Grant and client ID's are only stored for create routes
await grantReferenceService.create(
{
id: grant.grant,
clientId: grant.clientId
},
trx
)
}
})
ctx.grant = grant

// Unless the relevant grant action is ReadAll/ListAll add the
Expand Down
10 changes: 0 additions & 10 deletions packages/backend/src/open_payments/connection/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,20 @@ import {
import { createIncomingPayment } from '../../tests/incomingPayment'
import { createPaymentPointer } from '../../tests/paymentPointer'
import base64url from 'base64url'
import { GrantReference } from '../grantReference/model'
import { GrantReferenceService } from '../grantReference/service'

describe('Connection Routes', (): void => {
let deps: IocContract<AppServices>
let appContainer: TestContainer
let knex: Knex
let config: IAppConfig
let connectionRoutes: ConnectionRoutes
let grantReferenceService: GrantReferenceService
let grantRef: GrantReference

beforeAll(async (): Promise<void> => {
config = Config
config.authServerGrantUrl = 'https://auth.wallet.example/authorize'
deps = await initIocContainer(config)
appContainer = await createTestApp(deps)
knex = await deps.use('knex')
grantReferenceService = await deps.use('grantReferenceService')
jestOpenAPI(await deps.use('openApi'))
})

Expand All @@ -51,13 +46,8 @@ describe('Connection Routes', (): void => {
config = await deps.use('config')

paymentPointer = await createPaymentPointer(deps, { asset })
grantRef = await grantReferenceService.create({
id: uuid(),
clientId: uuid()
})
incomingPayment = await createIncomingPayment(deps, {
paymentPointerId: paymentPointer.id,
grantId: grantRef.id,
description: 'hello world',
expiresAt: new Date(Date.now() + 30_000),
incomingAmount: {
Expand Down
Loading