@uphold/fastify-openapi-router-plugin 0.1.0
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/LICENSE +21 -0
- package/README.md +268 -0
- package/package.json +56 -0
- package/src/errors/index.js +9 -0
- package/src/errors/scopes-mismatch-error.js +17 -0
- package/src/errors/unauthorized-error.js +16 -0
- package/src/index.js +8 -0
- package/src/index.test.js +93 -0
- package/src/parser/body.js +19 -0
- package/src/parser/body.test.js +95 -0
- package/src/parser/index.js +54 -0
- package/src/parser/params.js +24 -0
- package/src/parser/params.test.js +72 -0
- package/src/parser/response.js +14 -0
- package/src/parser/response.test.js +137 -0
- package/src/parser/security.js +123 -0
- package/src/parser/security.test.js +522 -0
- package/src/parser/spec.js +14 -0
- package/src/parser/spec.test.js +91 -0
- package/src/parser/url.js +4 -0
- package/src/parser/url.test.js +11 -0
- package/src/plugin.js +51 -0
- package/src/utils/constants.js +3 -0
- package/src/utils/schema.js +29 -0
- package/src/utils/schema.test.js +135 -0
- package/src/utils/security.js +80 -0
- package/src/utils/security.test.js +241 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const addPropertyToSchema = (schema, properties, required) => {
|
|
2
|
+
for (const key in properties) {
|
|
3
|
+
const property = properties[key];
|
|
4
|
+
|
|
5
|
+
// Respect the previous definition of the property.
|
|
6
|
+
if (!schema.properties[key]) {
|
|
7
|
+
schema.properties[key] = property;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Make the property required.
|
|
11
|
+
if (required && !schema.required.includes(key)) {
|
|
12
|
+
schema.required.push(key);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const removeAttributesFromSchema = (schema, attributes) => {
|
|
18
|
+
if (!attributes || !Array.isArray(attributes)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const prop in schema) {
|
|
23
|
+
if (attributes.includes(prop)) {
|
|
24
|
+
delete schema[prop];
|
|
25
|
+
} else if (typeof schema[prop] === 'object') {
|
|
26
|
+
removeAttributesFromSchema(schema[prop], attributes);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { addPropertyToSchema, removeAttributesFromSchema } from './schema.js';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('addPropertyToSchema()', () => {
|
|
5
|
+
it('should add a property as required to a schema', async () => {
|
|
6
|
+
const schema = {
|
|
7
|
+
properties: {
|
|
8
|
+
foo: { type: 'string' }
|
|
9
|
+
},
|
|
10
|
+
required: []
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
addPropertyToSchema(schema, { bar: { type: 'string' } }, true);
|
|
14
|
+
|
|
15
|
+
expect(schema).toStrictEqual({
|
|
16
|
+
properties: {
|
|
17
|
+
bar: { type: 'string' },
|
|
18
|
+
foo: { type: 'string' }
|
|
19
|
+
},
|
|
20
|
+
required: ['bar']
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should add a property as optional to a schema', async () => {
|
|
25
|
+
const schema = {
|
|
26
|
+
properties: {
|
|
27
|
+
foo: { type: 'string' }
|
|
28
|
+
},
|
|
29
|
+
required: ['foo']
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
addPropertyToSchema(schema, { bar: { type: 'string' } }, false);
|
|
33
|
+
|
|
34
|
+
expect(schema).toStrictEqual({
|
|
35
|
+
properties: {
|
|
36
|
+
bar: { type: 'string' },
|
|
37
|
+
foo: { type: 'string' }
|
|
38
|
+
},
|
|
39
|
+
required: ['foo']
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should not override a property that already exists', async () => {
|
|
44
|
+
const schema = {
|
|
45
|
+
properties: {
|
|
46
|
+
bar: { type: 'object' },
|
|
47
|
+
foo: { type: 'string' }
|
|
48
|
+
},
|
|
49
|
+
required: ['foo']
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
addPropertyToSchema(schema, { foo: { type: 'object' } }, false);
|
|
53
|
+
|
|
54
|
+
expect(schema).toStrictEqual({
|
|
55
|
+
properties: {
|
|
56
|
+
bar: { type: 'object' },
|
|
57
|
+
foo: { type: 'string' }
|
|
58
|
+
},
|
|
59
|
+
required: ['foo']
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should add multiple properties', async () => {
|
|
64
|
+
const schema = {
|
|
65
|
+
properties: {},
|
|
66
|
+
required: []
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
addPropertyToSchema(schema, { bar: { type: 'string' }, foo: { type: 'object' } }, false);
|
|
70
|
+
|
|
71
|
+
expect(schema).toStrictEqual({
|
|
72
|
+
properties: {
|
|
73
|
+
bar: { type: 'string' },
|
|
74
|
+
foo: { type: 'object' }
|
|
75
|
+
},
|
|
76
|
+
required: []
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('removeAttributesFromSchema()', () => {
|
|
82
|
+
it('should not throw error with invalid arguments', async () => {
|
|
83
|
+
const t = () => {
|
|
84
|
+
removeAttributesFromSchema();
|
|
85
|
+
removeAttributesFromSchema(undefined, []);
|
|
86
|
+
removeAttributesFromSchema({}, {});
|
|
87
|
+
removeAttributesFromSchema({}, ['foo']);
|
|
88
|
+
removeAttributesFromSchema({ foo: {} });
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
expect(t).not.toThrow();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should recursively remove unwanted attributes from a schema', async () => {
|
|
95
|
+
const schema = {
|
|
96
|
+
example: null,
|
|
97
|
+
properties: {
|
|
98
|
+
bar: {
|
|
99
|
+
example: 'pending',
|
|
100
|
+
type: 'string'
|
|
101
|
+
},
|
|
102
|
+
baz: {
|
|
103
|
+
example: undefined,
|
|
104
|
+
type: 'string',
|
|
105
|
+
xml: {
|
|
106
|
+
name: 'xml-title'
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
foo: {
|
|
110
|
+
example: 'approved',
|
|
111
|
+
type: 'string'
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
type: 'object',
|
|
115
|
+
xml: { name: 'xml-schema' }
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
removeAttributesFromSchema(schema, ['xml', 'example']);
|
|
119
|
+
|
|
120
|
+
expect(schema).toStrictEqual({
|
|
121
|
+
properties: {
|
|
122
|
+
bar: {
|
|
123
|
+
type: 'string'
|
|
124
|
+
},
|
|
125
|
+
baz: {
|
|
126
|
+
type: 'string'
|
|
127
|
+
},
|
|
128
|
+
foo: {
|
|
129
|
+
type: 'string'
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
type: 'object'
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createScopesMismatchError } from '../errors/index.js';
|
|
2
|
+
|
|
3
|
+
const getValueForHttpSchemeType = (request, securityScheme) => {
|
|
4
|
+
if (securityScheme.scheme === 'bearer') {
|
|
5
|
+
const [, bearer] = request.headers.authorization?.match(/^Bearer (.+)$/i) ?? [];
|
|
6
|
+
|
|
7
|
+
return bearer;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (securityScheme.scheme === 'basic') {
|
|
11
|
+
const [, basic] = request.headers.authorization?.match(/^Basic (.+)$/i) ?? [];
|
|
12
|
+
|
|
13
|
+
if (!basic) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const basicDecoded = Buffer.from(basic, 'base64').toString();
|
|
18
|
+
const [, username, password] = basicDecoded.match(/^([^:]*):(.*)$/) ?? [];
|
|
19
|
+
|
|
20
|
+
if (!username || !password) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { password, username };
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getValueForApiKeySchemeType = (request, securityScheme) => {
|
|
29
|
+
let source;
|
|
30
|
+
|
|
31
|
+
if (securityScheme.in === 'cookie') {
|
|
32
|
+
source = request.cookies;
|
|
33
|
+
} else if (securityScheme.in === 'header') {
|
|
34
|
+
source = request.headers;
|
|
35
|
+
} else if (securityScheme.in === 'query') {
|
|
36
|
+
source = request.query;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return source?.[securityScheme.name];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const getValueForOAuthOrOpenIdConnectSchemeType = request => {
|
|
43
|
+
const [, bearer] = request.headers.authorization?.match(/^Bearer (.+)$/i) ?? [];
|
|
44
|
+
|
|
45
|
+
return bearer;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const extractSecuritySchemeValueFromRequest = (request, securityScheme) => {
|
|
49
|
+
if (securityScheme.type === 'oauth2' || securityScheme.type === 'openIdConnect') {
|
|
50
|
+
return getValueForOAuthOrOpenIdConnectSchemeType(request);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (securityScheme.type === 'apiKey') {
|
|
54
|
+
return getValueForApiKeySchemeType(request, securityScheme);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (securityScheme.type === 'http') {
|
|
58
|
+
return getValueForHttpSchemeType(request, securityScheme);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const verifyScopes = (providedScopes, requiredScopes) => {
|
|
63
|
+
const missingScopes = requiredScopes.filter(requiredScope => {
|
|
64
|
+
const hasMatchingScope = providedScopes.some(providedScope => {
|
|
65
|
+
if (providedScope.endsWith('*')) {
|
|
66
|
+
const prefixScope = providedScope.match(/(.*)\*$/)[1];
|
|
67
|
+
|
|
68
|
+
return requiredScope.startsWith(prefixScope);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return providedScope === requiredScope;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return !hasMatchingScope;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (missingScopes.length > 0) {
|
|
78
|
+
throw createScopesMismatchError(providedScopes, requiredScopes, missingScopes);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { errors } from '../errors/index';
|
|
3
|
+
import { extractSecuritySchemeValueFromRequest, verifyScopes } from './security';
|
|
4
|
+
import _ from 'lodash-es';
|
|
5
|
+
|
|
6
|
+
describe('verifyScopes()', () => {
|
|
7
|
+
const runTest = ({ missing, provided, required }) => {
|
|
8
|
+
try {
|
|
9
|
+
verifyScopes(provided, required);
|
|
10
|
+
|
|
11
|
+
if (missing.length > 0) {
|
|
12
|
+
throw new Error('Expected an error to be thrown');
|
|
13
|
+
}
|
|
14
|
+
} catch (err) {
|
|
15
|
+
expect(err).toBeInstanceOf(errors.ScopesMismatchError);
|
|
16
|
+
expect(err).toMatchObject({
|
|
17
|
+
scopes: {
|
|
18
|
+
missing: missing,
|
|
19
|
+
provided: provided,
|
|
20
|
+
required: required
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
it('should verify regular scopes correctly against required scopes', async () => {
|
|
27
|
+
const provided = ['read', 'write'];
|
|
28
|
+
|
|
29
|
+
runTest({ missing: [], provided, required: [] });
|
|
30
|
+
runTest({ missing: [], provided, required: ['read', 'write'] });
|
|
31
|
+
runTest({ missing: [], provided, required: ['read'] });
|
|
32
|
+
runTest({ missing: ['remove'], provided, required: ['read', 'write', 'remove'] });
|
|
33
|
+
runTest({ missing: ['remove'], provided: ['read'], required: ['remove'] });
|
|
34
|
+
runTest({ missing: [''], provided, required: [''] });
|
|
35
|
+
runTest({ missing: ['read', 'write'], provided: [], required: ['read', 'write'] });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should verify regular scopes correctly against *', async () => {
|
|
39
|
+
const provided = ['*'];
|
|
40
|
+
|
|
41
|
+
runTest({ missing: [], provided, required: ['user'] });
|
|
42
|
+
runTest({ missing: [], provided, required: ['user:'] });
|
|
43
|
+
runTest({ missing: [], provided, required: ['user:foo'] });
|
|
44
|
+
runTest({ missing: [], provided, required: ['user:foo:'] });
|
|
45
|
+
runTest({ missing: [], provided, required: ['user:foo:bar'] });
|
|
46
|
+
runTest({ missing: [], provided, required: ['user:foo:bar:'] });
|
|
47
|
+
runTest({ missing: [], provided, required: [''] });
|
|
48
|
+
runTest({ missing: [], provided, required: [] });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should verify regular scopes correctly against * suffix', async () => {
|
|
52
|
+
let provided = ['user:*', 'transaction:foo:*'];
|
|
53
|
+
|
|
54
|
+
runTest({ missing: [], provided, required: ['user:foo'] });
|
|
55
|
+
runTest({ missing: [], provided, required: ['user:foo:bar'] });
|
|
56
|
+
runTest({ missing: [], provided, required: ['user:'] });
|
|
57
|
+
runTest({ missing: [], provided, required: ['transaction:foo:bar'] });
|
|
58
|
+
runTest({ missing: [], provided, required: ['transaction:foo:'] });
|
|
59
|
+
runTest({ missing: ['user'], provided, required: ['user'] });
|
|
60
|
+
runTest({ missing: ['transaction:foo'], provided, required: ['transaction:foo'] });
|
|
61
|
+
runTest({ missing: ['transaction::'], provided, required: ['transaction::'] });
|
|
62
|
+
runTest({ missing: [''], provided, required: [''] });
|
|
63
|
+
|
|
64
|
+
provided = ['user.*', 'transaction.foo.*'];
|
|
65
|
+
|
|
66
|
+
runTest({ missing: [], provided, required: ['user.foo'] });
|
|
67
|
+
runTest({ missing: [], provided, required: ['user.foo.bar'] });
|
|
68
|
+
runTest({ missing: [], provided, required: ['user.'] });
|
|
69
|
+
runTest({ missing: [], provided, required: ['transaction.foo.bar'] });
|
|
70
|
+
runTest({ missing: [], provided, required: ['transaction.foo.'] });
|
|
71
|
+
runTest({ missing: ['user'], provided, required: ['user'] });
|
|
72
|
+
runTest({ missing: ['transaction.foo'], provided, required: ['transaction.foo'] });
|
|
73
|
+
runTest({ missing: ['transaction..'], provided, required: ['transaction..'] });
|
|
74
|
+
runTest({ missing: [''], provided, required: [''] });
|
|
75
|
+
|
|
76
|
+
provided = ['user*', 'transaction:foo*'];
|
|
77
|
+
|
|
78
|
+
runTest({ missing: [], provided, required: ['userfoo'] });
|
|
79
|
+
runTest({ missing: [], provided, required: ['user'] });
|
|
80
|
+
runTest({ missing: [], provided, required: ['transaction:foo'] });
|
|
81
|
+
runTest({ missing: [], provided, required: ['transaction:foobar'] });
|
|
82
|
+
runTest({ missing: ['use'], provided, required: ['use'] });
|
|
83
|
+
runTest({ missing: ['transaction:foz'], provided, required: ['transaction:foz'] });
|
|
84
|
+
runTest({ missing: [''], provided, required: [''] });
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('extractSecuritySchemeValueFromRequest()', () => {
|
|
89
|
+
describe('oauth2', () => {
|
|
90
|
+
const securityScheme = { type: 'oauth2' };
|
|
91
|
+
|
|
92
|
+
it('should extract the bearer token from the authorization header', async () => {
|
|
93
|
+
const request1 = _.set({}, 'headers.authorization', 'Bearer foo bar');
|
|
94
|
+
const request2 = _.set({}, 'headers.authorization', 'bearer foo bar');
|
|
95
|
+
|
|
96
|
+
expect(extractSecuritySchemeValueFromRequest(request1, securityScheme)).toBe('foo bar');
|
|
97
|
+
expect(extractSecuritySchemeValueFromRequest(request2, securityScheme)).toBe('foo bar');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return undefined if the authorization header is malformed', async () => {
|
|
101
|
+
const request = _.set({}, 'headers.authorization', 'foo bar');
|
|
102
|
+
|
|
103
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should return undefined if the authorization header is missing', async () => {
|
|
107
|
+
const request = _.set({}, 'headers', {});
|
|
108
|
+
|
|
109
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('apiKey', () => {
|
|
114
|
+
describe('in: cookie', () => {
|
|
115
|
+
const securityScheme = { in: 'cookie', name: 'foo', type: 'apiKey' };
|
|
116
|
+
|
|
117
|
+
it('should extract the value from the cookie', async () => {
|
|
118
|
+
const request = _.set({}, 'cookies.foo', 'bar');
|
|
119
|
+
|
|
120
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBe('bar');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should return undefined if the cookie is missing', async () => {
|
|
124
|
+
const request = _.set({}, 'cookies', {});
|
|
125
|
+
|
|
126
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should return undefined if the are no cookies', async () => {
|
|
130
|
+
const request = {};
|
|
131
|
+
|
|
132
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('in: header', () => {
|
|
137
|
+
const securityScheme = { in: 'header', name: 'foo', type: 'apiKey' };
|
|
138
|
+
|
|
139
|
+
it('should extract the value from the header', async () => {
|
|
140
|
+
const request = _.set({}, 'headers.foo', 'bar');
|
|
141
|
+
|
|
142
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBe('bar');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should return undefined if the header is missing', async () => {
|
|
146
|
+
const request = _.set({}, 'headers', {});
|
|
147
|
+
|
|
148
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should return undefined if the are no headers', async () => {
|
|
152
|
+
const request = {};
|
|
153
|
+
|
|
154
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('in: query', () => {
|
|
159
|
+
const securityScheme = { in: 'query', name: 'foo', type: 'apiKey' };
|
|
160
|
+
|
|
161
|
+
it('should extract the value from the query', async () => {
|
|
162
|
+
const request = _.set({}, 'query.foo', 'bar');
|
|
163
|
+
|
|
164
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBe('bar');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should return undefined if the query is missing', async () => {
|
|
168
|
+
const request = _.set({}, 'query', {});
|
|
169
|
+
|
|
170
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should return undefined if the are no queries', async () => {
|
|
174
|
+
const request = {};
|
|
175
|
+
|
|
176
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('http', () => {
|
|
182
|
+
describe('scheme: bearer', () => {
|
|
183
|
+
const securityScheme = { scheme: 'bearer', type: 'http' };
|
|
184
|
+
|
|
185
|
+
it('should extract the bearer token from the authorization header', async () => {
|
|
186
|
+
const request1 = _.set({}, 'headers.authorization', 'Bearer foo bar');
|
|
187
|
+
const request2 = _.set({}, 'headers.authorization', 'bearer foo bar');
|
|
188
|
+
|
|
189
|
+
expect(extractSecuritySchemeValueFromRequest(request1, securityScheme)).toBe('foo bar');
|
|
190
|
+
expect(extractSecuritySchemeValueFromRequest(request2, securityScheme)).toBe('foo bar');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should return undefined if the authorization header is malformed', async () => {
|
|
194
|
+
const request = _.set({}, 'headers.authorization', 'foo bar');
|
|
195
|
+
|
|
196
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should return undefined if the authorization header is missing', async () => {
|
|
200
|
+
const request = _.set({}, 'headers', {});
|
|
201
|
+
|
|
202
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('scheme: basic', () => {
|
|
207
|
+
const securityScheme = { scheme: 'basic', type: 'http' };
|
|
208
|
+
|
|
209
|
+
it('should extract the username and password from the authorization header', async () => {
|
|
210
|
+
const request = _.set({}, 'headers.authorization', 'Basic Zm9vOmJhcg==');
|
|
211
|
+
|
|
212
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toStrictEqual({
|
|
213
|
+
password: 'bar',
|
|
214
|
+
username: 'foo'
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should return undefined if the authorization header is malformed', async () => {
|
|
219
|
+
const request1 = _.set({}, 'headers.authorization', 'foo');
|
|
220
|
+
const request2 = _.set({}, 'headers.authorization', 'Basic foo');
|
|
221
|
+
const request3 = _.set({}, 'headers.authorization', 'Basic _~#');
|
|
222
|
+
|
|
223
|
+
expect(extractSecuritySchemeValueFromRequest(request1, securityScheme)).toBeUndefined();
|
|
224
|
+
expect(extractSecuritySchemeValueFromRequest(request2, securityScheme)).toBeUndefined();
|
|
225
|
+
expect(extractSecuritySchemeValueFromRequest(request3, securityScheme)).toBeUndefined();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('should return undefined if the authorization header is missing', async () => {
|
|
229
|
+
const request = _.set({}, 'headers', {});
|
|
230
|
+
|
|
231
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should return undefined if the username or password is missing', async () => {
|
|
235
|
+
const request = _.set({}, 'headers.authorization', 'Basic Zm9v');
|
|
236
|
+
|
|
237
|
+
expect(extractSecuritySchemeValueFromRequest(request, securityScheme)).toBeUndefined();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
});
|