@lucaapp/service-utils 5.29.0 → 5.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/api/openapi.js +31 -5
- package/package.json +1 -1
package/dist/lib/api/openapi.js
CHANGED
|
@@ -225,6 +225,27 @@ const hoistCombinatorRequiredFields = (spec) => {
|
|
|
225
225
|
visit(schemas);
|
|
226
226
|
};
|
|
227
227
|
exports.hoistCombinatorRequiredFields = hoistCombinatorRequiredFields;
|
|
228
|
+
/**
|
|
229
|
+
* Turn each middleware's declared schemes into OpenAPI security requirements.
|
|
230
|
+
*
|
|
231
|
+
* OpenAPI expresses the two relations differently: separate objects in the
|
|
232
|
+
* `security` array are alternatives (OR), while several schemes inside one
|
|
233
|
+
* object are all required (AND). A middleware's own `security: [a, b]` means
|
|
234
|
+
* "a or b" -- it accepts either credential -- but two middlewares on the same
|
|
235
|
+
* route both run, so their requirements are combined with AND.
|
|
236
|
+
*
|
|
237
|
+
* That makes the result the cartesian product across middlewares:
|
|
238
|
+
*
|
|
239
|
+
* [[a, b], [c]] -> [{a, c}, {b, c}]
|
|
240
|
+
*
|
|
241
|
+
* A single middleware therefore still renders as plain alternatives, which is
|
|
242
|
+
* what it did before, and stacked middlewares no longer render as if presenting
|
|
243
|
+
* any one of their credentials were enough.
|
|
244
|
+
*/
|
|
245
|
+
const buildSecurityRequirements = (securityByMiddleware) => securityByMiddleware.reduce((requirements, alternatives) => requirements.flatMap(requirement =>
|
|
246
|
+
// Duplicates collapse on their own: the same scheme required twice is
|
|
247
|
+
// still one entry in the object.
|
|
248
|
+
alternatives.map(scheme => ({ ...requirement, [scheme]: [] }))), [{}]);
|
|
228
249
|
const registerEndpoint = (registry, method, prefixPath, rootPrefixPath, path, summary,
|
|
229
250
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
230
251
|
options, tags, debug) => {
|
|
@@ -233,7 +254,11 @@ options, tags, debug) => {
|
|
|
233
254
|
let querySchema = options.schemas?.query || undefined;
|
|
234
255
|
let headersSchema = options.schemas?.headers || undefined;
|
|
235
256
|
let responseSchemas = options.responses;
|
|
236
|
-
|
|
257
|
+
// One entry per security-declaring middleware, each holding that middleware's
|
|
258
|
+
// alternatives. Kept separate because the two levels mean different things:
|
|
259
|
+
// within a middleware the schemes are alternatives, across middlewares they
|
|
260
|
+
// are all enforced. See buildSecurityRequirements.
|
|
261
|
+
const securityByMiddleware = [];
|
|
237
262
|
if (options.errors) {
|
|
238
263
|
for (const errorType of Object.keys(options.errors)) {
|
|
239
264
|
const statusCode = options.errors[errorType];
|
|
@@ -294,8 +319,9 @@ options, tags, debug) => {
|
|
|
294
319
|
});
|
|
295
320
|
}
|
|
296
321
|
}
|
|
297
|
-
|
|
298
|
-
|
|
322
|
+
const middlewareSecurity = middleware.options.security || [];
|
|
323
|
+
if (middlewareSecurity.length > 0) {
|
|
324
|
+
securityByMiddleware.push([...middlewareSecurity]);
|
|
299
325
|
}
|
|
300
326
|
}
|
|
301
327
|
// Add ZodError response
|
|
@@ -355,8 +381,8 @@ options, tags, debug) => {
|
|
|
355
381
|
responses: responseMap,
|
|
356
382
|
operationId: options.operationId ||
|
|
357
383
|
deriveOperationId(registry, method, prefixPath.slice(rootPrefixPath.length) + path),
|
|
358
|
-
...(
|
|
359
|
-
security:
|
|
384
|
+
...(securityByMiddleware.length > 0 && {
|
|
385
|
+
security: buildSecurityRequirements(securityByMiddleware),
|
|
360
386
|
}),
|
|
361
387
|
});
|
|
362
388
|
};
|