Skip to content

Commit a8b4c69

Browse files
authored
fix: merge tenant jwks with new jwks table content (#669)
1 parent e2d9bf1 commit a8b4c69

File tree

4 files changed

+41
-8
lines changed

4 files changed

+41
-8
lines changed

src/http/routes/admin/tenants.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
resetMigration,
1818
runMigrationsOnTenant,
1919
} from '@internal/database/migrations'
20-
import { getConfig } from '../../../config'
20+
import { getConfig, JwksConfigKey } from '../../../config'
2121

2222
const patchSchema = {
2323
body: {
@@ -27,6 +27,7 @@ const patchSchema = {
2727
databaseUrl: { type: 'string' },
2828
databasePoolUrl: { type: 'string', nullable: true },
2929
maxConnections: { type: 'number' },
30+
jwks: { type: 'object', nullable: true },
3031
fileSizeLimit: { type: 'number' },
3132
jwtSecret: { type: 'string' },
3233
serviceKey: { type: 'string' },
@@ -89,6 +90,7 @@ interface tenantDBInterface {
8990
database_pool_url?: string
9091
max_connections?: number
9192
jwt_secret: string
93+
jwks: { keys?: JwksConfigKey[] } | null
9294
service_key: string
9395
file_size_limit?: number
9496
feature_s3_protocol?: boolean
@@ -113,6 +115,7 @@ export default async function routes(fastify: FastifyInstance) {
113115
max_connections,
114116
file_size_limit,
115117
jwt_secret,
118+
jwks,
116119
service_key,
117120
feature_purge_cache,
118121
feature_image_transformation,
@@ -130,6 +133,7 @@ export default async function routes(fastify: FastifyInstance) {
130133
maxConnections: max_connections ? Number(max_connections) : undefined,
131134
fileSizeLimit: Number(file_size_limit),
132135
jwtSecret: decrypt(jwt_secret),
136+
jwks,
133137
serviceKey: decrypt(service_key),
134138
migrationVersion: migrations_version,
135139
migrationStatus: migrations_status,
@@ -163,7 +167,7 @@ export default async function routes(fastify: FastifyInstance) {
163167
max_connections,
164168
file_size_limit,
165169
jwt_secret,
166-
170+
jwks,
167171
service_key,
168172
feature_purge_cache,
169173
feature_s3_protocol,
@@ -187,7 +191,7 @@ export default async function routes(fastify: FastifyInstance) {
187191
maxConnections: max_connections ? Number(max_connections) : undefined,
188192
fileSizeLimit: Number(file_size_limit),
189193
jwtSecret: decrypt(jwt_secret),
190-
194+
jwks,
191195
serviceKey: decrypt(service_key),
192196
features: {
193197
imageTransformation: {
@@ -215,6 +219,7 @@ export default async function routes(fastify: FastifyInstance) {
215219
databaseUrl,
216220
fileSizeLimit,
217221
jwtSecret,
222+
jwks,
218223
serviceKey,
219224
features,
220225
databasePoolUrl,
@@ -231,6 +236,7 @@ export default async function routes(fastify: FastifyInstance) {
231236
max_connections: maxConnections ? Number(maxConnections) : undefined,
232237
file_size_limit: fileSizeLimit,
233238
jwt_secret: encrypt(jwtSecret),
239+
jwks,
234240
service_key: encrypt(serviceKey),
235241
feature_image_transformation: features?.imageTransformation?.enabled ?? false,
236242
feature_purge_cache: features?.purgeCache?.enabled ?? false,
@@ -270,6 +276,7 @@ export default async function routes(fastify: FastifyInstance) {
270276
databaseUrl,
271277
fileSizeLimit,
272278
jwtSecret,
279+
jwks,
273280
serviceKey,
274281
features,
275282
databasePoolUrl,
@@ -291,6 +298,7 @@ export default async function routes(fastify: FastifyInstance) {
291298
max_connections: maxConnections ? Number(maxConnections) : undefined,
292299
file_size_limit: fileSizeLimit,
293300
jwt_secret: jwtSecret !== undefined ? encrypt(jwtSecret) : undefined,
301+
jwks,
294302
service_key: serviceKey !== undefined ? encrypt(serviceKey) : undefined,
295303
feature_image_transformation: features?.imageTransformation?.enabled,
296304
feature_purge_cache: features?.purgeCache?.enabled,
@@ -335,6 +343,7 @@ export default async function routes(fastify: FastifyInstance) {
335343
databaseUrl,
336344
fileSizeLimit,
337345
jwtSecret,
346+
jwks,
338347
serviceKey,
339348
features,
340349
databasePoolUrl,
@@ -350,6 +359,7 @@ export default async function routes(fastify: FastifyInstance) {
350359
anon_key: encrypt(anonKey),
351360
database_url: encrypt(databaseUrl),
352361
jwt_secret: encrypt(jwtSecret),
362+
jwks: jwks || null,
353363
service_key: encrypt(serviceKey),
354364
}
355365

src/internal/database/tenant.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import crypto from 'node:crypto'
2-
import { getConfig, JwksConfig, JwksConfigKeyOCT } from '../../config'
2+
import { getConfig, JwksConfig, JwksConfigKey, JwksConfigKeyOCT } from '../../config'
33
import { decrypt, encrypt, verifyJWT } from '../auth'
44
import { multitenantKnex } from './multitenant-db'
55
import { JwtPayload } from 'jsonwebtoken'
@@ -20,6 +20,7 @@ interface TenantConfig {
2020
fileSizeLimit: number
2121
features: Features
2222
jwtSecret: string
23+
jwks?: { keys: JwksConfigKey[] } | null
2324
serviceKey: string
2425
serviceKeyPayload: {
2526
role: string
@@ -215,10 +216,12 @@ export async function getJwtSecret(
215216
let jwks = jwtJWKS || { keys: [] }
216217

217218
if (isMultitenant) {
218-
const [config, tenantJwks] = await Promise.all([
219-
getTenantConfig(tenantId),
220-
jwksManager.getJwksTenantConfig(tenantId),
221-
])
219+
const config = await getTenantConfig(tenantId)
220+
const tenantJwks = await jwksManager.getJwksTenantConfig(tenantId)
221+
if (config.jwks?.keys) {
222+
// merge jwks from legacy jwks column if they exist
223+
tenantJwks.keys = [...tenantJwks.keys, ...config.jwks.keys]
224+
}
222225
secret = config.jwtSecret
223226
jwks = tenantJwks
224227
}

src/test/tenant-jwks.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,24 @@ describe('Tenant jwks configs', () => {
178178
}
179179
})
180180

181+
test(`Add ${type} jwk via tenant patch (legacy)`, async () => {
182+
const patchResponse = await adminApp.inject({
183+
method: 'PATCH',
184+
url: `/tenants/${tenantId}`,
185+
payload: {
186+
jwks: { keys: [jwk] },
187+
},
188+
headers: {
189+
apikey: process.env.ADMIN_API_KEYS,
190+
},
191+
})
192+
expect(patchResponse.statusCode).toBe(204)
193+
194+
const { jwks } = await getJwtSecret(tenantId)
195+
expect(jwks.keys.length).toBe(2)
196+
expect(jwks.keys[1]).toEqual(jwk)
197+
})
198+
181199
test(`Add ${type} jwk with missing data`, async () => {
182200
const response = await adminApp.inject({
183201
method: 'POST',

src/test/tenant.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const payload = {
1818
fileSizeLimit: 1,
1919
jwtSecret: 'c',
2020
serviceKey: 'd',
21+
jwks: { keys: [] },
2122
migrationStatus: 'COMPLETED',
2223
migrationVersion: 'optimise-existing-functions',
2324
tracingMode: 'basic',
@@ -44,6 +45,7 @@ const payload2 = {
4445
fileSizeLimit: 2,
4546
jwtSecret: 'g',
4647
serviceKey: 'h',
48+
jwks: null,
4749
migrationStatus: 'COMPLETED',
4850
migrationVersion: 'optimise-existing-functions',
4951
tracingMode: 'basic',

0 commit comments

Comments
 (0)