Skip to content

Commit 4a932d4

Browse files
refactor: only register callback routes when their processor is active
1 parent d8f62b4 commit 4a932d4

9 files changed

Lines changed: 28 additions & 91 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"nostream": patch
3+
---
4+
5+
refactor: only register OpenNode, LNbits, and Zebedee callback routes when their processor is active

src/controllers/callbacks/lnbits-callback-controller.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ export class LNbitsCallbackController implements IController {
2525

2626
const settings = createSettings()
2727
const remoteAddress = getRemoteAddress(request, settings)
28-
const paymentProcessor = settings.payments?.processor ?? 'null'
29-
30-
if (paymentProcessor !== 'lnbits') {
31-
logger('denied request from %s to /callbacks/lnbits which is not the current payment processor', remoteAddress)
32-
response.status(403).send('Forbidden')
33-
return
34-
}
3528

3629
const queryValidation = validateSchema(lnbitsCallbackQuerySchema)(request.query)
3730
if (queryValidation.error) {

src/controllers/callbacks/opennode-callback-controller.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,6 @@ export class OpenNodeCallbackController implements IController {
2222

2323
const settings = createSettings()
2424
const remoteAddress = getRemoteAddress(request, settings)
25-
const paymentProcessor = settings.payments?.processor
26-
27-
if (paymentProcessor !== 'opennode') {
28-
logger('denied request from %s to /callbacks/opennode which is not the current payment processor', remoteAddress)
29-
response
30-
.status(403)
31-
.send('Forbidden')
32-
return
33-
}
3425

3526
const bodyValidation = validateSchema(opennodeWebhookCallbackBodySchema)(request.body)
3627
if (bodyValidation.error) {

src/controllers/callbacks/zebedee-callback-controller.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,13 @@ export class ZebedeeCallbackController implements IController {
3030

3131
const { ipWhitelist = [] } = settings.paymentsProcessors?.zebedee ?? {}
3232
const remoteAddress = getRemoteAddress(request, settings)
33-
const paymentProcessor = settings.payments?.processor
3433

3534
if (ipWhitelist.length && !ipWhitelist.includes(remoteAddress)) {
3635
logger('unauthorized request from %s to /callbacks/zebedee', remoteAddress)
3736
response.status(403).send('Forbidden')
3837
return
3938
}
4039

41-
if (paymentProcessor !== 'zebedee') {
42-
logger('denied request from %s to /callbacks/zebedee which is not the current payment processor', remoteAddress)
43-
response.status(403).send('Forbidden')
44-
return
45-
}
46-
4740
const invoice = fromZebedeeInvoice(request.body)
4841

4942
logger('invoice', invoice)

src/routes/callbacks/index.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { json, Router, urlencoded } from 'express'
1+
import { json, NextFunction, Request, Response, Router, urlencoded } from 'express'
22

33
import { createLNbitsCallbackController } from '../../factories/controllers/lnbits-callback-controller-factory'
44
import { createNodelessCallbackController } from '../../factories/controllers/nodeless-callback-controller-factory'
@@ -9,24 +9,30 @@ import { withController } from '../../handlers/request-handlers/with-controller-
99

1010
const router: Router = Router()
1111

12-
const settings = createSettings()
13-
const processor = settings.payments?.processor
12+
const requireProcessor = (name: string) =>
13+
(_req: Request, res: Response, next: NextFunction) => {
14+
const settings = createSettings()
15+
if (settings.payments?.processor !== name) {
16+
res.status(403).send('Forbidden')
17+
return
18+
}
19+
next()
20+
}
1421

1522
router
16-
.post('/zebedee', json(), withController(createZebedeeCallbackController))
17-
.post('/lnbits', json(), withController(createLNbitsCallbackController))
18-
.post('/opennode', urlencoded({ extended: false }), json(), withController(createOpenNodeCallbackController))
19-
20-
if (processor === 'nodeless') {
21-
router.post(
23+
.post('/zebedee', requireProcessor('zebedee'), json(), withController(createZebedeeCallbackController))
24+
.post('/lnbits', requireProcessor('lnbits'), json(), withController(createLNbitsCallbackController))
25+
.post('/opennode', requireProcessor('opennode'), urlencoded({ extended: false }), json(), withController(createOpenNodeCallbackController))
26+
.post(
2227
'/nodeless',
28+
requireProcessor('nodeless'),
2329
json({
2430
verify(req, _res, buf) {
2531
;(req as any).rawBody = buf
2632
},
2733
}),
2834
withController(createNodelessCallbackController),
2935
)
30-
}
3136

3237
export default router
38+

test/unit/controllers/callbacks/lnbits-callback-controller.spec.ts

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -108,35 +108,6 @@ describe('LNbitsCallbackController', () => {
108108
})
109109

110110
describe('authorization and validation', () => {
111-
it('returns 403 when payment processor settings are missing', async () => {
112-
createSettingsStub.returns({
113-
network: { remoteIpHeader: 'x-forwarded-for' },
114-
})
115-
const { controller, paymentsService } = makeController()
116-
const res = makeRes()
117-
118-
await controller.handleRequest(makeReq(), res)
119-
120-
expect(res.status).to.have.been.calledWith(403)
121-
expect(res.send).to.have.been.calledWith('Forbidden')
122-
expect(paymentsService.getInvoiceFromPaymentsProcessor).to.not.have.been.called
123-
})
124-
125-
it('returns 403 when lnbits is not the configured processor', async () => {
126-
createSettingsStub.returns({
127-
...baseSettings,
128-
payments: { processor: 'opennode' },
129-
})
130-
const { controller, paymentsService } = makeController()
131-
const res = makeRes()
132-
133-
await controller.handleRequest(makeReq(), res)
134-
135-
expect(res.status).to.have.been.calledWith(403)
136-
expect(res.send).to.have.been.calledWith('Forbidden')
137-
expect(paymentsService.getInvoiceFromPaymentsProcessor).to.not.have.been.called
138-
})
139-
140111
it('returns 403 for invalid query parameters', async () => {
141112
const { controller } = makeController()
142113
const res = makeRes()

test/unit/controllers/callbacks/opennode-callback-controller.spec.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,6 @@ describe('OpenNodeCallbackController', () => {
9999
})
100100

101101
describe('authorization and validation', () => {
102-
it('returns 403 when opennode is not the configured processor', async () => {
103-
createSettingsStub.returns({
104-
payments: { processor: 'lnbits' },
105-
})
106-
const { controller, paymentsService } = makeController()
107-
const res = makeRes()
108-
109-
await controller.handleRequest(makeReq(), res)
110-
111-
expect(res.status).to.have.been.calledWith(403)
112-
expect(res.send).to.have.been.calledWith('Forbidden')
113-
expect(paymentsService.updateInvoiceStatus).to.not.have.been.called
114-
})
115-
116102
it('returns 400 for malformed request body', async () => {
117103
const { controller, paymentsService } = makeController()
118104
const res = makeRes()

test/unit/controllers/callbacks/zebedee-callback-controller.spec.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,6 @@ describe('ZebedeeCallbackController', () => {
136136
expect(res.send).to.have.been.calledWith('Forbidden')
137137
expect(paymentsService.updateInvoiceStatus).to.not.have.been.called
138138
})
139-
140-
it('returns 403 when zebedee is not the configured processor', async () => {
141-
createSettingsStub.returns({
142-
...baseSettings,
143-
payments: { processor: 'lnbits' },
144-
})
145-
const { controller, paymentsService } = makeController()
146-
const res = makeRes()
147-
148-
await controller.handleRequest(makeReq(), res)
149-
150-
expect(res.status).to.have.been.calledWith(403)
151-
expect(res.send).to.have.been.calledWith('Forbidden')
152-
expect(paymentsService.updateInvoiceStatus).to.not.have.been.called
153-
})
154139
})
155140

156141
describe('invoice state handling', () => {

test/unit/routes/callbacks.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@ import express from 'express'
44
import Sinon from 'sinon'
55

66
import * as openNodeControllerFactory from '../../../src/factories/controllers/opennode-callback-controller-factory'
7+
import * as settingsFactory from '../../../src/factories/settings-factory'
78

89
describe('callbacks router', () => {
910
let createOpenNodeCallbackControllerStub: Sinon.SinonStub
11+
let createSettingsStub: Sinon.SinonStub
1012
let receivedBody: unknown
1113
let server: any
1214

1315
beforeEach(async () => {
1416
receivedBody = undefined
1517

18+
createSettingsStub = Sinon.stub(settingsFactory, 'createSettings').returns({
19+
payments: { processor: 'opennode' },
20+
} as any)
21+
1622
createOpenNodeCallbackControllerStub = Sinon.stub(openNodeControllerFactory, 'createOpenNodeCallbackController').returns({
1723
handleRequest: async (request: any, response: any) => {
1824
receivedBody = request.body
@@ -35,6 +41,7 @@ describe('callbacks router', () => {
3541

3642
afterEach(async () => {
3743
createOpenNodeCallbackControllerStub.restore()
44+
createSettingsStub.restore()
3845
delete require.cache[require.resolve('../../../src/routes/callbacks')]
3946

4047
if (server) {

0 commit comments

Comments
 (0)