Skip to content

Commit ef010f7

Browse files
feat(respect): resolve and apply x security to step (#2076)
1 parent c3a60e0 commit ef010f7

File tree

4 files changed

+65
-2
lines changed

4 files changed

+65
-2
lines changed

packages/respect-core/src/modules/description-parser/get-operation-by-id.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function getOperationById(
3232
}
3333

3434
const description = descriptions[descriptionName];
35+
const securitySchemes = description?.components?.securitySchemes;
3536
const rootServers = description.servers;
3637

3738
for (const [path, pathDetails] of Object.entries(descriptions[descriptionName].paths)) {
@@ -48,6 +49,7 @@ export function getOperationById(
4849
path,
4950
method,
5051
descriptionName,
52+
securitySchemes,
5153
};
5254
}
5355
}

packages/respect-core/src/modules/description-parser/get-operation-by-path.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export function getOperationByPath(
3838
}
3939

4040
const description = $sourceDescriptions[descriptionName] || {};
41+
const securitySchemes = description?.components?.securitySchemes;
42+
4143
const [prop, path, method] = JsonPointerLib.parse(fragmentIdentifier);
4244

4345
if (prop !== 'paths') {
@@ -60,5 +62,6 @@ export function getOperationByPath(
6062
path,
6163
method,
6264
descriptionName,
65+
securitySchemes,
6366
};
6467
}

packages/respect-core/src/modules/flow-runner/prepare-request.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { getServerUrl } from './get-server-url.js';
1313
import { createRuntimeExpressionCtx, collectSecretFields } from './context/index.js';
1414
import { evaluateRuntimeExpressionPayload } from '../runtime-expressions/index.js';
15+
import { resolveXSecurityParameters } from './resolve-x-security-parameters.js';
1516

1617
import type { ParameterWithIn } from '../config-parser/index.js';
1718
import type { TestContext, Step, Parameter, PublicStep } from '../../types.js';
@@ -139,7 +140,8 @@ export async function prepareRequest(
139140
step,
140141
});
141142

142-
const evaluatedParameters = parameters.map((parameter) => {
143+
const xSecurityParameters = resolveXSecurityParameters(expressionContext, step, openapiOperation);
144+
const evaluatedParameters = joinParameters(parameters, xSecurityParameters).map((parameter) => {
143145
return {
144146
...parameter,
145147
value: evaluateRuntimeExpressionPayload({
@@ -196,7 +198,8 @@ function joinParameters(...parameters: ParameterWithIn[][]): ParameterWithIn[] {
196198
const parametersWithNames = parameters.flat().filter((param) => 'name' in param);
197199

198200
const parameterMap = parametersWithNames.reduce((map, param) => {
199-
map[param.name] = param;
201+
const key = `${param.name}:${param.in}`;
202+
map[key] = param;
200203
return map;
201204
}, {} as { [key: string]: ParameterWithIn });
202205

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { evaluateRuntimeExpressionPayload } from '../runtime-expressions/index.js';
2+
3+
import type { ParameterWithIn } from '../config-parser/index.js';
4+
import type { Step, RuntimeExpressionContext } from '../../types.js';
5+
import type { OperationDetails } from '../description-parser/get-operation-from-description.js';
6+
7+
export function resolveXSecurityParameters(
8+
ctx: RuntimeExpressionContext,
9+
step: Step,
10+
operation: (OperationDetails & Record<string, any>) | undefined
11+
) {
12+
const { 'x-security': xSecurity } = step;
13+
const xSecurityParameters: ParameterWithIn[] = [];
14+
15+
if (!xSecurity) {
16+
return xSecurityParameters;
17+
}
18+
19+
for (const securityScheme of xSecurity) {
20+
const { schemeName, scheme, values } = securityScheme;
21+
22+
const resolvedValues = Object.entries(values || {}).reduce<Record<string, any>>(
23+
(acc, [key, value]) => ({
24+
...acc,
25+
[key]: evaluateRuntimeExpressionPayload({ payload: value, context: ctx }),
26+
}),
27+
{}
28+
);
29+
30+
let resolvedSchema = null;
31+
if (schemeName) {
32+
resolvedSchema = operation?.securitySchemes[schemeName] || null;
33+
} else {
34+
resolvedSchema = scheme;
35+
}
36+
37+
// TODO: replace with the Auth algorithm functions
38+
if (resolvedSchema?.type === 'apiKey' && resolvedValues?.value) {
39+
xSecurityParameters.push({
40+
name: resolvedSchema.name,
41+
in: resolvedSchema.in,
42+
value: resolvedValues.value as string | number | boolean,
43+
});
44+
} else if (resolvedSchema?.type === 'http' && resolvedSchema?.scheme === 'basic') {
45+
const { username, password } = resolvedValues;
46+
xSecurityParameters.push({
47+
name: 'Authorization',
48+
in: 'header',
49+
value: `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
50+
});
51+
}
52+
}
53+
54+
return xSecurityParameters;
55+
}

0 commit comments

Comments
 (0)