From 7bed6f2acf4ada0c784d8fbc726683906b88cac8 Mon Sep 17 00:00:00 2001 From: Abraham Ingersoll <586805+aberoham@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:43:53 +0100 Subject: [PATCH] zone_record: write to the id in the path PUT /zone_record/{id} passed the path id first and spread the body over it, so a body id replaced it. The handler read the record named in the URL, found it, then wrote to the record named in the body and answered 200. before: 7314 address=203.0.113.2 PUT /zone_record/7313 {id: 7314, address: 198.51.100.99} -> 200 after: 7313 address=203.0.113.1 named in the URL, untouched after: 7314 address=198.51.100.99 never named in the URL Only zone_record accepts an id in a PUT body. The zone and nameserver schemas refuse the key, so those two routes answer 400 before the handler runs. Their argument order matches group and user now, and the route tests reach their update handlers for the first time. --- routes/nameserver.js | 2 +- routes/nameserver.test.js | 12 ++++++++++++ routes/zone.js | 2 +- routes/zone.test.js | 12 ++++++++++++ routes/zone_record.js | 2 +- routes/zone_record.test.js | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/routes/nameserver.js b/routes/nameserver.js index 3fb4ea1..1e19b0e 100644 --- a/routes/nameserver.js +++ b/routes/nameserver.js @@ -86,7 +86,7 @@ function NameserverRoutes(server) { return h.response({ meta: { api: meta.api, msg: `I couldn't find that nameserver` } }).code(404) } - await Nameserver.put({ id, ...request.payload }) + await Nameserver.put({ ...request.payload, id }) const updated = await Nameserver.get({ id }) return h diff --git a/routes/nameserver.test.js b/routes/nameserver.test.js index ef80f95..187cd82 100644 --- a/routes/nameserver.test.js +++ b/routes/nameserver.test.js @@ -78,6 +78,18 @@ describe('nameserver routes', () => { assert.ok(res.result.nameserver[0].gid) }) + it(`PUT /nameserver/${case2Id}`, async () => { + const res = await server.inject({ + method: 'PUT', + url: `/nameserver/${case2Id}`, + headers: auth.headers, + payload: { description: 'edited by the route test' }, + }) + + assert.equal(res.statusCode, 200) + assert.equal((await Nameserver.get({ id: case2Id }))[0].description, 'edited by the route test') + }) + it(`DELETE /nameserver/${case2Id}`, async () => { const res = await server.inject({ method: 'DELETE', diff --git a/routes/zone.js b/routes/zone.js index a3462d0..7d5b014 100644 --- a/routes/zone.js +++ b/routes/zone.js @@ -124,7 +124,7 @@ function ZoneRoutes(server) { return h.response({ meta: { api: meta.api, msg: `I couldn't find that zone` } }).code(404) } - await Zone.put({ id, ...request.payload }) + await Zone.put({ ...request.payload, id }) const updated = await Zone.get({ id }) return h.response({ zone: updated, meta: { api: meta.api, msg: `the zone was updated` } }).code(200) diff --git a/routes/zone.test.js b/routes/zone.test.js index 86c3eb7..71f1444 100644 --- a/routes/zone.test.js +++ b/routes/zone.test.js @@ -103,6 +103,18 @@ describe('zone routes', () => { assert.ok(res.result.zone[0].gid) }) + it(`PUT /zone/${case2Id}`, async () => { + const res = await server.inject({ + method: 'PUT', + url: `/zone/${case2Id}`, + headers: auth.headers, + payload: { description: 'edited by the route test' }, + }) + + assert.equal(res.statusCode, 200) + assert.equal((await Zone.get({ id: case2Id }))[0].description, 'edited by the route test') + }) + it(`DELETE /zone/${case2Id}`, async () => { const res = await server.inject({ method: 'DELETE', diff --git a/routes/zone_record.js b/routes/zone_record.js index 285cb33..5d9a7ea 100644 --- a/routes/zone_record.js +++ b/routes/zone_record.js @@ -134,7 +134,7 @@ function ZoneRecordRoutes(server) { return h.response({ meta: { api: meta.api, msg: `I couldn't find that zone record` } }).code(404) } - await ZoneRecord.put({ id, ...request.payload }) + await ZoneRecord.put({ ...request.payload, id }) const updated = await ZoneRecord.get({ id }) return h diff --git a/routes/zone_record.test.js b/routes/zone_record.test.js index 5e96845..fc59dd9 100644 --- a/routes/zone_record.test.js +++ b/routes/zone_record.test.js @@ -17,6 +17,7 @@ const createdZoneRecordIds = [] const testGroupId = 5094 const testZoneId = 5095 const testZoneRecordId = 5096 +const bystanderId = 5097 const testZone = { ...zoneCase, @@ -51,6 +52,12 @@ before(async () => { await User.create(testUser) await Zone.create(testZone) await ZoneRecord.create(testZoneRecord) + await ZoneRecord.create({ + ...testZoneRecord, + id: bystanderId, + owner: 'bystander.route-zr-delete.example.com.', + address: '203.0.113.9', + }) server = await init() }) @@ -60,6 +67,7 @@ after(async () => { await ZoneRecord.destroy({ id }) } await ZoneRecord.destroy({ id: testZoneRecordId }) + await ZoneRecord.destroy({ id: bystanderId }) await Zone.destroy({ id: testZoneId }) await server.stop() }) @@ -170,6 +178,31 @@ describe('zone_record routes', () => { assert.ok(res.result.meta.pagination.total >= 3) }) + it(`PUT /zone_record/${testZoneRecordId} updates the record in the path`, async () => { + const res = await server.inject({ + method: 'PUT', + url: `/zone_record/${testZoneRecordId}`, + headers: auth.headers, + payload: { address: '198.51.100.99' }, + }) + + assert.equal(res.statusCode, 200) + assert.equal((await ZoneRecord.get({ id: testZoneRecordId }))[0].address, '198.51.100.99') + }) + + // asserts on the bystander alone, so a schema that refuses a payload id + // leaves this passing rather than pinning the status code + it(`PUT /zone_record/${testZoneRecordId} leaves ${bystanderId} alone`, async () => { + await server.inject({ + method: 'PUT', + url: `/zone_record/${testZoneRecordId}`, + headers: auth.headers, + payload: { id: bystanderId, address: '198.51.100.50' }, + }) + + assert.equal((await ZoneRecord.get({ id: bystanderId }))[0].address, '203.0.113.9') + }) + it(`DELETE /zone_record/${testZoneRecordId} soft-deletes record`, async () => { const res = await server.inject({ method: 'DELETE',