postman-runtime 7.20.1 → 7.21.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/.eslintrc +1 -1
- package/CHANGELOG.yaml +26 -0
- package/lib/authorizer/basic.js +1 -3
- package/lib/authorizer/ntlm.js +79 -4
- package/lib/authorizer/oauth2.js +25 -20
- package/lib/requester/core.js +19 -5
- package/lib/runner/extensions/event.command.js +7 -1
- package/lib/runner/extensions/item.command.js +39 -4
- package/package.json +7 -8
- package/test/fixtures/server.js +123 -9
- package/test/integration/auth-methods/ntlm.test.js +232 -6
- package/test/integration/benchmark/large-response.test.js +79 -0
- package/test/integration/sandbox-libraries/pm.test.js +55 -0
- package/test/integration/sanity/proxy-http.test.js +69 -0
- package/test/unit/auth-handlers.test.js +64 -37
package/.eslintrc
CHANGED
package/CHANGELOG.yaml
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
7.21.0:
|
|
2
|
+
date: 2019-12-02
|
|
3
|
+
new features:
|
|
4
|
+
- >-
|
|
5
|
+
GH-937 Added ability to use OAuth2 tokens with unknown types as Bearer
|
|
6
|
+
token in OAuth2 authentication
|
|
7
|
+
- GH-934 Added ability to pull domain name from username for NTLM auth
|
|
8
|
+
fixed bugs:
|
|
9
|
+
- >-
|
|
10
|
+
GH-939 Fixed a bug where IPv6 localhost request through IPv4 localhost
|
|
11
|
+
proxy was failing.
|
|
12
|
+
- >-
|
|
13
|
+
GH-928 Fixed a bug where Basic Auth was failing if credentials had
|
|
14
|
+
non-ASCII characters
|
|
15
|
+
- >-
|
|
16
|
+
GH-929 Fixed a bug where error was thrown when `username` or `password`
|
|
17
|
+
fields were empty for NTLM auth
|
|
18
|
+
- >-
|
|
19
|
+
GH-933 Fixed a bug where NTLM could not complete authentication if
|
|
20
|
+
multiple `www-authenticate` headers were sent
|
|
21
|
+
chores:
|
|
22
|
+
- >-
|
|
23
|
+
GH-942 Convert response into a JSON serializable object before executing
|
|
24
|
+
the script
|
|
25
|
+
- Updated dependencies
|
|
26
|
+
|
|
1
27
|
7.20.1:
|
|
2
28
|
date: 2019-11-13
|
|
3
29
|
chores:
|
package/lib/authorizer/basic.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
var btoa = require('btoa');
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @implements {AuthHandlerInterface}
|
|
5
3
|
*/
|
|
@@ -70,7 +68,7 @@ module.exports = {
|
|
|
70
68
|
request.removeHeader('Authorization', {ignoreCase: true});
|
|
71
69
|
request.addHeader({
|
|
72
70
|
key: 'Authorization',
|
|
73
|
-
value: 'Basic ' +
|
|
71
|
+
value: 'Basic ' + Buffer.from(`${username}:${password}`, 'utf8').toString('base64'),
|
|
74
72
|
system: true
|
|
75
73
|
});
|
|
76
74
|
|
package/lib/authorizer/ntlm.js
CHANGED
|
@@ -31,6 +31,58 @@ var ntlmUtil = require('httpntlm').ntlm,
|
|
|
31
31
|
T3_MSG_CREATED: 'T3_MSG_CREATED'
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Parses the username to separate username and domain. It can handle two formats:
|
|
36
|
+
* - Down-Level Logon name format `DOMAIN\USERNAME`
|
|
37
|
+
* - User Principal Name format `USERNAME@DOMAIN`
|
|
38
|
+
*
|
|
39
|
+
* @param {String} username - Username string to parse from
|
|
40
|
+
* @return {Object} - An object with `username` and `domain` fields, which are `strings`.
|
|
41
|
+
*/
|
|
42
|
+
function parseParametersFromUsername (username) {
|
|
43
|
+
var dllParams,
|
|
44
|
+
upnParams;
|
|
45
|
+
|
|
46
|
+
if (!(username && typeof username === 'string')) {
|
|
47
|
+
return {
|
|
48
|
+
username: EMPTY,
|
|
49
|
+
domain: EMPTY
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dllParams = username.split('\\');
|
|
54
|
+
upnParams = username.split('@');
|
|
55
|
+
|
|
56
|
+
// username should be either of the two formats, not both
|
|
57
|
+
if (dllParams.length > 1 && upnParams.length > 1) {
|
|
58
|
+
return {
|
|
59
|
+
username,
|
|
60
|
+
domain: EMPTY
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// try to parse from "down level logon" format
|
|
65
|
+
if (dllParams.length === 2 && dllParams[0] && dllParams[1]) {
|
|
66
|
+
return {
|
|
67
|
+
username: dllParams[1],
|
|
68
|
+
domain: dllParams[0]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// try to parse from "user principal name" format
|
|
73
|
+
if (upnParams.length === 2 && upnParams[0] && upnParams[1]) {
|
|
74
|
+
return {
|
|
75
|
+
username: upnParams[0],
|
|
76
|
+
domain: upnParams[1]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
username,
|
|
82
|
+
domain: EMPTY
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
34
86
|
/**
|
|
35
87
|
* NTLM auth while authenticating requires negotiateMessage (type 1) and authenticateMessage (type 3) to be stored.
|
|
36
88
|
* Also it needs to know which stage is it in (INITIALIZED, T1_MSG_CREATED and T3_MSG_CREATED).
|
|
@@ -96,16 +148,26 @@ module.exports = {
|
|
|
96
148
|
var state = auth.get(STATE),
|
|
97
149
|
domain = auth.get(NTLM_PARAMETERS.DOMAIN) || EMPTY,
|
|
98
150
|
workstation = auth.get(NTLM_PARAMETERS.WORKSTATION) || EMPTY,
|
|
99
|
-
username = auth.get(NTLM_PARAMETERS.USERNAME),
|
|
100
|
-
password = auth.get(NTLM_PARAMETERS.PASSWORD),
|
|
151
|
+
username = auth.get(NTLM_PARAMETERS.USERNAME) || EMPTY,
|
|
152
|
+
password = auth.get(NTLM_PARAMETERS.PASSWORD) || EMPTY,
|
|
101
153
|
negotiateMessage, // type 1
|
|
102
154
|
challengeMessage, // type 2
|
|
103
|
-
authenticateMessage
|
|
155
|
+
authenticateMessage, // type 3
|
|
156
|
+
ntlmType2Header,
|
|
157
|
+
parsedParameters;
|
|
104
158
|
|
|
105
159
|
if (response.code !== 401 && response.code !== 403) {
|
|
106
160
|
return done(null, true);
|
|
107
161
|
}
|
|
108
162
|
|
|
163
|
+
// we try to extract domain from username if not specified.
|
|
164
|
+
if (!domain) {
|
|
165
|
+
parsedParameters = parseParametersFromUsername(username) || {};
|
|
166
|
+
|
|
167
|
+
username = parsedParameters.username;
|
|
168
|
+
domain = parsedParameters.domain;
|
|
169
|
+
}
|
|
170
|
+
|
|
109
171
|
if (state === STATES.INITIALIZED) {
|
|
110
172
|
// Nothing to do if the server does not ask us for auth in the first place.
|
|
111
173
|
if (!(response.headers.has(WWW_AUTHENTICATE, NTLM) ||
|
|
@@ -130,7 +192,20 @@ module.exports = {
|
|
|
130
192
|
}
|
|
131
193
|
else if (state === STATES.T1_MSG_CREATED) {
|
|
132
194
|
// At this point, we can assume that the type 1 message was sent to the server
|
|
133
|
-
|
|
195
|
+
|
|
196
|
+
// there can be multiple headers present with key `www-authenticate`.
|
|
197
|
+
// iterate to get the one which has the NTLM hash. if multiple
|
|
198
|
+
// headers have the NTLM hash, use the first one.
|
|
199
|
+
ntlmType2Header = response.headers.find(function (header) {
|
|
200
|
+
return String(header.key).toLowerCase() === WWW_AUTHENTICATE &&
|
|
201
|
+
header.valueOf().startsWith('NTLM ');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (!ntlmType2Header) {
|
|
205
|
+
return done(new Error('ntlm: server did not send NTLM type 2 message'));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
challengeMessage = ntlmUtil.parseType2Message(ntlmType2Header.valueOf(), _.noop);
|
|
134
209
|
|
|
135
210
|
if (!challengeMessage) {
|
|
136
211
|
return done(new Error('ntlm: server did not correctly process authentication request'));
|
package/lib/authorizer/oauth2.js
CHANGED
|
@@ -3,6 +3,7 @@ var _ = require('lodash'),
|
|
|
3
3
|
HEADER = 'header',
|
|
4
4
|
QUERY_PARAMS = 'queryParams',
|
|
5
5
|
BEARER = 'bearer',
|
|
6
|
+
MAC = 'mac',
|
|
6
7
|
AUTHORIZATION = 'Authorization',
|
|
7
8
|
ACCESS_TOKEN = 'access_token',
|
|
8
9
|
AUTHORIZATION_PREFIX = 'Bearer ',
|
|
@@ -94,27 +95,31 @@ module.exports = {
|
|
|
94
95
|
tokenType = _.toLower(params.tokenType);
|
|
95
96
|
|
|
96
97
|
// @TODO Add support for HMAC
|
|
97
|
-
if (tokenType ===
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// and clear them before the sign step for any auth
|
|
101
|
-
request.removeHeader(AUTHORIZATION, {ignoreCase: true});
|
|
102
|
-
request.removeQueryParams([ACCESS_TOKEN]);
|
|
98
|
+
if (tokenType === MAC) {
|
|
99
|
+
return done();
|
|
100
|
+
}
|
|
103
101
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
102
|
+
// treat every token types (other than MAC) as bearer token
|
|
103
|
+
|
|
104
|
+
// clean conflicting headers and query params
|
|
105
|
+
// @todo: we should be able to get conflicting params from auth manifest
|
|
106
|
+
// and clear them before the sign step for any auth
|
|
107
|
+
request.removeHeader(AUTHORIZATION, {ignoreCase: true});
|
|
108
|
+
request.removeQueryParams([ACCESS_TOKEN]);
|
|
109
|
+
|
|
110
|
+
if (params.addTokenTo === QUERY_PARAMS) {
|
|
111
|
+
request.addQueryParams({
|
|
112
|
+
key: ACCESS_TOKEN,
|
|
113
|
+
value: params.accessToken,
|
|
114
|
+
system: true
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else if (params.addTokenTo === HEADER) {
|
|
118
|
+
request.addHeader({
|
|
119
|
+
key: AUTHORIZATION,
|
|
120
|
+
value: AUTHORIZATION_PREFIX + params.accessToken,
|
|
121
|
+
system: true
|
|
122
|
+
});
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
return done();
|
package/lib/requester/core.js
CHANGED
|
@@ -251,12 +251,14 @@ module.exports = {
|
|
|
251
251
|
self = this,
|
|
252
252
|
bodyParams,
|
|
253
253
|
url = request.url && urlEncoder.toNodeUrl(request.url.toString(true)),
|
|
254
|
-
isSSL,
|
|
254
|
+
isSSL = _.startsWith(url.protocol, HTTPS),
|
|
255
|
+
isTunnelingProxy = request.proxy && (request.proxy.tunnel || isSSL),
|
|
255
256
|
reqOption,
|
|
256
257
|
portNumber,
|
|
257
258
|
behaviorName,
|
|
258
259
|
port = url && url.port,
|
|
259
|
-
hostname = url && url.hostname && url.hostname.toLowerCase()
|
|
260
|
+
hostname = url && url.hostname && url.hostname.toLowerCase(),
|
|
261
|
+
proxyHostname = request.proxy && request.proxy.host;
|
|
260
262
|
|
|
261
263
|
!defaultOpts && (defaultOpts = {});
|
|
262
264
|
!protocolProfileBehavior && (protocolProfileBehavior = {});
|
|
@@ -268,6 +270,10 @@ module.exports = {
|
|
|
268
270
|
hostname = LOCALHOST;
|
|
269
271
|
}
|
|
270
272
|
|
|
273
|
+
if (getTLD(proxyHostname) === LOCALHOST) {
|
|
274
|
+
proxyHostname = LOCALHOST;
|
|
275
|
+
}
|
|
276
|
+
|
|
271
277
|
options.url = url;
|
|
272
278
|
options.method = request.method;
|
|
273
279
|
options.jar = defaultOpts.cookieJar || true;
|
|
@@ -334,9 +340,17 @@ module.exports = {
|
|
|
334
340
|
self.ensureHeaderExists(options.headers, 'Host', url.host);
|
|
335
341
|
|
|
336
342
|
// override DNS lookup
|
|
337
|
-
if (networkOptions.restrictedAddresses || hostname === LOCALHOST ||
|
|
338
|
-
|
|
339
|
-
|
|
343
|
+
if (networkOptions.restrictedAddresses || hostname === LOCALHOST ||
|
|
344
|
+
(!isTunnelingProxy && proxyHostname === LOCALHOST) || networkOptions.hostLookup) {
|
|
345
|
+
// Use proxy port for localhost resolution in case of non-tunneling proxy
|
|
346
|
+
// because the request will be sent to proxy server by postman-request
|
|
347
|
+
if (request.proxy && !isTunnelingProxy) {
|
|
348
|
+
portNumber = Number(request.proxy.port);
|
|
349
|
+
}
|
|
350
|
+
// Otherwise, use request's port
|
|
351
|
+
else {
|
|
352
|
+
portNumber = Number(port) || (isSSL ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT);
|
|
353
|
+
}
|
|
340
354
|
|
|
341
355
|
_.isFinite(portNumber) && (options.lookup = lookup.bind(this, {
|
|
342
356
|
port: portNumber,
|
|
@@ -490,7 +490,13 @@ module.exports = {
|
|
|
490
490
|
result && result.collectionVariables &&
|
|
491
491
|
(result.collectionVariables = new sdk.VariableScope(result.collectionVariables));
|
|
492
492
|
result && result.request && (result.request = new sdk.Request(result.request));
|
|
493
|
-
|
|
493
|
+
|
|
494
|
+
// @note Since postman-sandbox@3.5.2, response object is not included in the execution result.
|
|
495
|
+
// Refer: https://github.com/postmanlabs/postman-sandbox/pull/512
|
|
496
|
+
// Adding back here to avoid breaking change in `script` callback.
|
|
497
|
+
// @todo revisit script callback args in runtime v8.
|
|
498
|
+
payload.context && payload.context.response &&
|
|
499
|
+
(result.response = new sdk.Response(payload.context.response));
|
|
494
500
|
|
|
495
501
|
// persist the pm.variables for the next script
|
|
496
502
|
result && result._variables &&
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
var _ = require('lodash'),
|
|
2
2
|
uuid = require('uuid'),
|
|
3
|
+
Response = require('postman-collection').Response,
|
|
3
4
|
visualizer = require('../../visualizer'),
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* List of request properties which can be mutated via
|
|
7
|
+
* List of request properties which can be mutated via pre-request
|
|
7
8
|
*
|
|
8
9
|
* @private
|
|
9
10
|
* @const
|
|
@@ -11,7 +12,8 @@ var _ = require('lodash'),
|
|
|
11
12
|
*/
|
|
12
13
|
ALLOWED_REQUEST_MUTATIONS = ['url', 'method', 'headers'],
|
|
13
14
|
|
|
14
|
-
extractVisualizerData
|
|
15
|
+
extractVisualizerData,
|
|
16
|
+
getResponseJSON;
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Returns visualizer data from the latest execution result.
|
|
@@ -36,7 +38,7 @@ extractVisualizerData = function (prereqExecutions, testExecutions) {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
if (_.isArray(prereqExecutions)) {
|
|
39
|
-
//
|
|
41
|
+
// extract visualizer data from pre-request script results if it is not found earlier
|
|
40
42
|
for (i = prereqExecutions.length - 1; i >= 0; i--) {
|
|
41
43
|
visualizerData = _.get(prereqExecutions[i], 'result.return.visualizer');
|
|
42
44
|
|
|
@@ -47,6 +49,31 @@ extractVisualizerData = function (prereqExecutions, testExecutions) {
|
|
|
47
49
|
}
|
|
48
50
|
};
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Convert response into a JSON serializable object.
|
|
54
|
+
* The stream property is converted to base64 string for performance reasons.
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} response - SDK Response instance
|
|
57
|
+
* @returns {Object}
|
|
58
|
+
*/
|
|
59
|
+
getResponseJSON = function (response) {
|
|
60
|
+
if (!Response.isResponse(response)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
id: response.id,
|
|
66
|
+
code: response.code,
|
|
67
|
+
status: response.status,
|
|
68
|
+
header: response.headers && response.headers.toJSON(),
|
|
69
|
+
stream: response.stream && {
|
|
70
|
+
type: 'Base64',
|
|
71
|
+
data: response.stream.toString('base64')
|
|
72
|
+
},
|
|
73
|
+
responseTime: response.responseTime
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
50
77
|
/**
|
|
51
78
|
* Add options
|
|
52
79
|
* stopOnError:Boolean
|
|
@@ -173,7 +200,15 @@ module.exports = {
|
|
|
173
200
|
|
|
174
201
|
// also the test object requires the updated request object (since auth helpers may modify it)
|
|
175
202
|
request && (ctxTemplate.request = request);
|
|
176
|
-
|
|
203
|
+
|
|
204
|
+
// @note convert response instance to plain object.
|
|
205
|
+
// we want to avoid calling Response.toJSON() which triggers toJSON on Response.stream buffer.
|
|
206
|
+
// Because that increases the size of stringified object by 3 times.
|
|
207
|
+
// Also, that increases the total number of tokens (buffer.data) whereas Buffer.toString
|
|
208
|
+
// generates a single string that is easier to stringify and sent over the UVM bridge.
|
|
209
|
+
response && (ctxTemplate.response = getResponseJSON(response));
|
|
210
|
+
|
|
211
|
+
// set cookies for this transaction
|
|
177
212
|
cookies && (ctxTemplate.cookies = cookies);
|
|
178
213
|
|
|
179
214
|
// the context template also has a test object to store assertions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postman-runtime",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.21.0",
|
|
4
4
|
"description": "Underlying library of executing Postman Collections (used by Newman)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -30,20 +30,19 @@
|
|
|
30
30
|
"license": "Apache-2.0",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"async": "2.6.2",
|
|
33
|
-
"aws4": "1.
|
|
34
|
-
"btoa": "1.2.1",
|
|
33
|
+
"aws4": "1.9.0",
|
|
35
34
|
"crypto-js": "3.1.9-1",
|
|
36
35
|
"eventemitter3": "4.0.0",
|
|
37
|
-
"handlebars": "4.5.
|
|
36
|
+
"handlebars": "4.5.3",
|
|
38
37
|
"http-reasons": "0.1.0",
|
|
39
38
|
"httpntlm": "1.7.6",
|
|
40
39
|
"inherits": "2.0.4",
|
|
41
40
|
"lodash": "4.17.15",
|
|
42
41
|
"node-oauth1": "1.2.2",
|
|
43
42
|
"performance-now": "2.1.0",
|
|
44
|
-
"postman-collection": "3.5.
|
|
43
|
+
"postman-collection": "3.5.5",
|
|
45
44
|
"postman-request": "2.88.1-postman.16",
|
|
46
|
-
"postman-sandbox": "3.5.
|
|
45
|
+
"postman-sandbox": "3.5.2",
|
|
47
46
|
"postman-url-encoder": "1.0.3",
|
|
48
47
|
"resolve-from": "5.0.0",
|
|
49
48
|
"serialised-error": "1.1.3",
|
|
@@ -58,14 +57,14 @@
|
|
|
58
57
|
"eslint": "5.16.0",
|
|
59
58
|
"eslint-plugin-jsdoc": "8.7.0",
|
|
60
59
|
"eslint-plugin-lodash": "5.1.0",
|
|
61
|
-
"eslint-plugin-mocha": "6.2.
|
|
60
|
+
"eslint-plugin-mocha": "6.2.2",
|
|
62
61
|
"eslint-plugin-security": "1.4.0",
|
|
63
62
|
"graphql": "14.5.8",
|
|
64
63
|
"http-proxy": "1.18.0",
|
|
65
64
|
"istanbul": "0.4.5",
|
|
66
65
|
"js-yaml": "3.13.1",
|
|
67
66
|
"jsdoc": "3.6.3",
|
|
68
|
-
"jsdoc-to-markdown": "5.0.
|
|
67
|
+
"jsdoc-to-markdown": "5.0.3",
|
|
69
68
|
"mocha": "6.2.2",
|
|
70
69
|
"parse-gitignore": "0.5.1",
|
|
71
70
|
"postman-jsdoc-theme": "0.0.3",
|
package/test/fixtures/server.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
const fs = require('fs'),
|
|
2
2
|
net = require('net'),
|
|
3
|
+
url = require('url'),
|
|
4
|
+
dns = require('dns'),
|
|
3
5
|
_ = require('lodash'),
|
|
4
6
|
path = require('path'),
|
|
5
7
|
http = require('http'),
|
|
6
8
|
https = require('https'),
|
|
7
9
|
crypto = require('crypto'),
|
|
8
10
|
GraphQL = require('graphql'),
|
|
11
|
+
ntlmUtils = require('httpntlm').ntlm,
|
|
9
12
|
enableServerDestroy = require('server-destroy');
|
|
10
13
|
|
|
11
14
|
/**
|
|
@@ -206,6 +209,7 @@ function createHTTPServer () {
|
|
|
206
209
|
* @param {Object} [options] - Additional options to configure proxy server
|
|
207
210
|
* @param {Object} [options.auth] - Proxy authentication, Basic auth
|
|
208
211
|
* @param {String} [options.agent] - Agent used for http(s).request
|
|
212
|
+
* @param {Boolean} [options.useIPv6] - If true, force using IPv6 address while forwarding request.
|
|
209
213
|
*
|
|
210
214
|
* @example
|
|
211
215
|
* var s = createProxyServer({
|
|
@@ -244,14 +248,22 @@ function createProxyServer (options) {
|
|
|
244
248
|
req.headers = Object.assign(req.headers, options.headers || {});
|
|
245
249
|
|
|
246
250
|
// forward request to the origin and pipe the response
|
|
247
|
-
var
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
var requestUrl = url.parse(req.url),
|
|
252
|
+
fwd = agent.request({
|
|
253
|
+
host: requestUrl.hostname,
|
|
254
|
+
path: requestUrl.path,
|
|
255
|
+
port: requestUrl.port,
|
|
256
|
+
method: req.method.toLowerCase(),
|
|
257
|
+
headers: req.headers,
|
|
258
|
+
lookup: options.useIPv6 && function (hostname, options, callback) {
|
|
259
|
+
!options && (options = {});
|
|
260
|
+
options.family = 6;
|
|
261
|
+
|
|
262
|
+
return dns.lookup(hostname, options, callback);
|
|
263
|
+
}
|
|
264
|
+
}, function (resp) {
|
|
265
|
+
resp.pipe(res);
|
|
266
|
+
});
|
|
255
267
|
|
|
256
268
|
req.pipe(fwd);
|
|
257
269
|
});
|
|
@@ -497,6 +509,106 @@ function createEdgeGridAuthServer (options) {
|
|
|
497
509
|
return server;
|
|
498
510
|
}
|
|
499
511
|
|
|
512
|
+
/**
|
|
513
|
+
* Creates an NTLM server.
|
|
514
|
+
*
|
|
515
|
+
* @param {Object} options - The options for the server
|
|
516
|
+
* @param {String} options.username - Username for authentication
|
|
517
|
+
* @param {String} options.password - Password for authentication
|
|
518
|
+
* @param {String} options.domain - Domain name for authentication
|
|
519
|
+
* @param {String} options.workstation - Workstation for authentication
|
|
520
|
+
* @param {Boolean} options.debug - Enable logging of requests
|
|
521
|
+
*
|
|
522
|
+
* @return {Object} - http server
|
|
523
|
+
*/
|
|
524
|
+
function createNTLMServer (options) {
|
|
525
|
+
options = options || {};
|
|
526
|
+
|
|
527
|
+
var type2Message = 'NTLM ' +
|
|
528
|
+
'TlRMTVNTUAACAAAAHgAeADgAAAAFgoqiBevywvJykjAAAAAAAAAAAJgAmABWAAAA' +
|
|
529
|
+
'CgC6RwAAAA9EAEUAUwBLAFQATwBQAC0ASgBTADQAVQBKAFQARAACAB4ARABFAFMA' +
|
|
530
|
+
'SwBUAE8AUAAtAEoAUwA0AFUASgBUAEQAAQAeAEQARQBTAEsAVABPAFAALQBKAFMA' +
|
|
531
|
+
'NABVAEoAVABEAAQAHgBEAEUAUwBLAFQATwBQAC0ASgBTADQAVQBKAFQARAADAB4A' +
|
|
532
|
+
'RABFAFMASwBUAE8AUAAtAEoAUwA0AFUASgBUAEQABwAIADmguzCHn9UBAAAAAA==',
|
|
533
|
+
parsedType2Message = ntlmUtils.parseType2Message(type2Message, _.noop),
|
|
534
|
+
|
|
535
|
+
username = options.username || 'username',
|
|
536
|
+
password = options.password || 'password',
|
|
537
|
+
domain = options.domain || '',
|
|
538
|
+
workstation = options.workstation || '',
|
|
539
|
+
|
|
540
|
+
type1Message = ntlmUtils.createType1Message({
|
|
541
|
+
domain,
|
|
542
|
+
workstation
|
|
543
|
+
}),
|
|
544
|
+
type3Message = ntlmUtils.createType3Message(parsedType2Message, {
|
|
545
|
+
domain,
|
|
546
|
+
workstation,
|
|
547
|
+
username,
|
|
548
|
+
password
|
|
549
|
+
}),
|
|
550
|
+
|
|
551
|
+
handler = function (req, res) {
|
|
552
|
+
var authHeaders = req.headers.authorization;
|
|
553
|
+
|
|
554
|
+
// send type2 message and ask for type3 message
|
|
555
|
+
if (authHeaders && authHeaders.startsWith(type1Message.slice(0, 20))) {
|
|
556
|
+
res.writeHead(401, {
|
|
557
|
+
|
|
558
|
+
// @note we're sending a 'Negotiate' header here to make
|
|
559
|
+
// sure that runtime can handle it.
|
|
560
|
+
'www-authenticate': [type2Message, 'Negotiate']
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
options.debug && console.info('401: got type1 message');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// successful auth
|
|
567
|
+
// @note we don't check if the username and password are correct
|
|
568
|
+
// because I don't know how.
|
|
569
|
+
else if (authHeaders && authHeaders.startsWith(type3Message.slice(0, 100))) {
|
|
570
|
+
res.writeHead(200);
|
|
571
|
+
|
|
572
|
+
options.debug && console.info('200: got type3 message');
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// no valid auth headers, ask for type1 message
|
|
576
|
+
else {
|
|
577
|
+
res.writeHead(401, {
|
|
578
|
+
'www-authenticate': ['NTLM', 'Negotiate']
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
options.debug && console.info('401: got no authorization header');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
res.end();
|
|
585
|
+
},
|
|
586
|
+
server = http.createServer(handler);
|
|
587
|
+
|
|
588
|
+
enableServerDestroy(server);
|
|
589
|
+
|
|
590
|
+
return server;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Custom junk bytes response server.
|
|
595
|
+
*
|
|
596
|
+
* `/${bytes}` returns binary response of given bytes size.
|
|
597
|
+
*/
|
|
598
|
+
function createBytesServer () {
|
|
599
|
+
var server = http.createServer(function (req, res) {
|
|
600
|
+
var bytes = Number(req.url.substr(1)) || 0; // remove leading /
|
|
601
|
+
|
|
602
|
+
res.writeHead(200);
|
|
603
|
+
res.write(Buffer.alloc(bytes));
|
|
604
|
+
res.end();
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
enableServerDestroy(server);
|
|
608
|
+
|
|
609
|
+
return server;
|
|
610
|
+
}
|
|
611
|
+
|
|
500
612
|
module.exports = {
|
|
501
613
|
createSSLServer,
|
|
502
614
|
createHTTPServer,
|
|
@@ -504,5 +616,7 @@ module.exports = {
|
|
|
504
616
|
createRawEchoServer,
|
|
505
617
|
createGraphQLServer,
|
|
506
618
|
createRedirectServer,
|
|
507
|
-
createEdgeGridAuthServer
|
|
619
|
+
createEdgeGridAuthServer,
|
|
620
|
+
createNTLMServer,
|
|
621
|
+
createBytesServer
|
|
508
622
|
};
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
var expect = require('chai').expect,
|
|
2
|
-
_ = require('lodash')
|
|
2
|
+
_ = require('lodash'),
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
server = require('../../fixtures/server');
|
|
5
|
+
|
|
6
|
+
describe('NTLM', function () {
|
|
5
7
|
// @todo Add '/ntlm' endpoint in echo server
|
|
6
|
-
var
|
|
8
|
+
var PORT = 2000,
|
|
7
9
|
USERNAME = 'postman',
|
|
8
10
|
PASSWORD = 'NTLM@123',
|
|
9
|
-
DOMAIN = '',
|
|
10
|
-
WORKSTATION = '',
|
|
11
|
+
DOMAIN = 'domain',
|
|
12
|
+
WORKSTATION = 'workstation',
|
|
13
|
+
ntlmServerURL = 'http://localhost:' + PORT,
|
|
14
|
+
ntlmServer,
|
|
11
15
|
testrun,
|
|
12
16
|
runOptions = {
|
|
13
17
|
collection: {
|
|
14
18
|
item: {
|
|
15
19
|
name: 'NTLM Sample Request',
|
|
16
20
|
request: {
|
|
17
|
-
url:
|
|
21
|
+
url: ntlmServerURL,
|
|
18
22
|
auth: {
|
|
19
23
|
type: 'ntlm',
|
|
20
24
|
ntlm: {
|
|
@@ -29,6 +33,20 @@ describe.skip('NTLM', function () {
|
|
|
29
33
|
}
|
|
30
34
|
};
|
|
31
35
|
|
|
36
|
+
before(function (done) {
|
|
37
|
+
ntlmServer = server.createNTLMServer({
|
|
38
|
+
// debug: true,
|
|
39
|
+
username: USERNAME,
|
|
40
|
+
password: PASSWORD,
|
|
41
|
+
domain: DOMAIN,
|
|
42
|
+
workstation: WORKSTATION
|
|
43
|
+
}).listen(PORT, done);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
after(function (done) {
|
|
47
|
+
ntlmServer.destroy(done);
|
|
48
|
+
});
|
|
49
|
+
|
|
32
50
|
describe('with request server not supporting NTLM', function () {
|
|
33
51
|
before(function (done) {
|
|
34
52
|
var clonedRunOptions = _.merge({}, runOptions, {
|
|
@@ -87,6 +105,58 @@ describe.skip('NTLM', function () {
|
|
|
87
105
|
});
|
|
88
106
|
});
|
|
89
107
|
|
|
108
|
+
describe('with empty details', function () {
|
|
109
|
+
before(function (done) {
|
|
110
|
+
// creating local copy of collection because we don't want to send
|
|
111
|
+
// any parameters for NTLM auth
|
|
112
|
+
var localRunOptions = {
|
|
113
|
+
collection: {
|
|
114
|
+
item: {
|
|
115
|
+
name: 'NTLM Sample Request',
|
|
116
|
+
request: {
|
|
117
|
+
url: ntlmServerURL,
|
|
118
|
+
auth: {
|
|
119
|
+
type: 'ntlm'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// perform the collection run
|
|
127
|
+
this.run(localRunOptions, function (err, results) {
|
|
128
|
+
testrun = results;
|
|
129
|
+
done(err);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should have completed the run', function () {
|
|
134
|
+
expect(testrun).to.be.ok;
|
|
135
|
+
expect(testrun).to.nested.include({
|
|
136
|
+
'done.callCount': 1
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
var err = testrun.request.firstCall.args[0];
|
|
140
|
+
|
|
141
|
+
err && console.error(err.stack);
|
|
142
|
+
expect(err).to.be.null;
|
|
143
|
+
|
|
144
|
+
expect(testrun).to.nested.include({
|
|
145
|
+
'start.callCount': 1
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should have sent the request thrice', function () {
|
|
150
|
+
expect(testrun).to.nested.include({
|
|
151
|
+
'request.callCount': 3
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
var response = testrun.request.firstCall.args[2];
|
|
155
|
+
|
|
156
|
+
expect(response).to.have.property('code', 401);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
90
160
|
describe('with in-correct details', function () {
|
|
91
161
|
before(function (done) {
|
|
92
162
|
var clonedRunOptions = _.merge({}, runOptions, {
|
|
@@ -253,4 +323,160 @@ describe.skip('NTLM', function () {
|
|
|
253
323
|
expect(response).to.have.property('code', 200);
|
|
254
324
|
});
|
|
255
325
|
});
|
|
326
|
+
|
|
327
|
+
describe('with username in down-level logon format', function () {
|
|
328
|
+
before(function (done) {
|
|
329
|
+
var clonedRunOptions = _.merge({}, runOptions, {
|
|
330
|
+
environment: {
|
|
331
|
+
values: [{
|
|
332
|
+
key: 'uname',
|
|
333
|
+
value: DOMAIN + '\\' + USERNAME
|
|
334
|
+
}, {
|
|
335
|
+
key: 'pass',
|
|
336
|
+
value: PASSWORD
|
|
337
|
+
}, {
|
|
338
|
+
key: 'domain',
|
|
339
|
+
value: ''
|
|
340
|
+
}, {
|
|
341
|
+
key: 'workstation',
|
|
342
|
+
value: WORKSTATION
|
|
343
|
+
}]
|
|
344
|
+
}
|
|
345
|
+
}, runOptions);
|
|
346
|
+
|
|
347
|
+
// perform the collection run
|
|
348
|
+
this.run(clonedRunOptions, function (err, results) {
|
|
349
|
+
testrun = results;
|
|
350
|
+
done(err);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should have completed the run successfully', function () {
|
|
355
|
+
expect(testrun).to.be.ok;
|
|
356
|
+
expect(testrun).to.nested.include({
|
|
357
|
+
'done.callCount': 1
|
|
358
|
+
});
|
|
359
|
+
testrun.done.getCall(0).args[0] && console.error(testrun.done.getCall(0).args[0].stack);
|
|
360
|
+
expect(testrun.done.getCall(0).args[0]).to.be.null;
|
|
361
|
+
expect(testrun).to.nested.include({
|
|
362
|
+
'start.callCount': 1
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should have sent the request thrice', function () {
|
|
367
|
+
expect(testrun).to.nested.include({
|
|
368
|
+
'request.callCount': 3
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
var err = testrun.request.thirdCall.args[0],
|
|
372
|
+
response = testrun.request.thirdCall.args[2];
|
|
373
|
+
|
|
374
|
+
expect(err).to.be.null;
|
|
375
|
+
expect(response).to.have.property('code', 200);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe('with username in user principal name format', function () {
|
|
380
|
+
before(function (done) {
|
|
381
|
+
var clonedRunOptions = _.merge({}, runOptions, {
|
|
382
|
+
environment: {
|
|
383
|
+
values: [{
|
|
384
|
+
key: 'uname',
|
|
385
|
+
value: USERNAME + '@' + DOMAIN
|
|
386
|
+
}, {
|
|
387
|
+
key: 'pass',
|
|
388
|
+
value: PASSWORD
|
|
389
|
+
}, {
|
|
390
|
+
key: 'domain',
|
|
391
|
+
value: ''
|
|
392
|
+
}, {
|
|
393
|
+
key: 'workstation',
|
|
394
|
+
value: WORKSTATION
|
|
395
|
+
}]
|
|
396
|
+
}
|
|
397
|
+
}, runOptions);
|
|
398
|
+
|
|
399
|
+
// perform the collection run
|
|
400
|
+
this.run(clonedRunOptions, function (err, results) {
|
|
401
|
+
testrun = results;
|
|
402
|
+
done(err);
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('should have completed the run successfully', function () {
|
|
407
|
+
expect(testrun).to.be.ok;
|
|
408
|
+
expect(testrun).to.nested.include({
|
|
409
|
+
'done.callCount': 1
|
|
410
|
+
});
|
|
411
|
+
testrun.done.getCall(0).args[0] && console.error(testrun.done.getCall(0).args[0].stack);
|
|
412
|
+
expect(testrun.done.getCall(0).args[0]).to.be.null;
|
|
413
|
+
expect(testrun).to.nested.include({
|
|
414
|
+
'start.callCount': 1
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('should have sent the request thrice', function () {
|
|
419
|
+
expect(testrun).to.nested.include({
|
|
420
|
+
'request.callCount': 3
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
var err = testrun.request.thirdCall.args[0],
|
|
424
|
+
response = testrun.request.thirdCall.args[2];
|
|
425
|
+
|
|
426
|
+
expect(err).to.be.null;
|
|
427
|
+
expect(response).to.have.property('code', 200);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
describe('with username in both formats', function () {
|
|
432
|
+
before(function (done) {
|
|
433
|
+
var clonedRunOptions = _.merge({}, runOptions, {
|
|
434
|
+
environment: {
|
|
435
|
+
values: [{
|
|
436
|
+
key: 'uname',
|
|
437
|
+
value: DOMAIN + '\\' + USERNAME + '@' + DOMAIN
|
|
438
|
+
}, {
|
|
439
|
+
key: 'pass',
|
|
440
|
+
value: PASSWORD
|
|
441
|
+
}, {
|
|
442
|
+
key: 'domain',
|
|
443
|
+
value: ''
|
|
444
|
+
}, {
|
|
445
|
+
key: 'workstation',
|
|
446
|
+
value: WORKSTATION
|
|
447
|
+
}]
|
|
448
|
+
}
|
|
449
|
+
}, runOptions);
|
|
450
|
+
|
|
451
|
+
// perform the collection run
|
|
452
|
+
this.run(clonedRunOptions, function (err, results) {
|
|
453
|
+
testrun = results;
|
|
454
|
+
done(err);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('should have completed the run successfully', function () {
|
|
459
|
+
expect(testrun).to.be.ok;
|
|
460
|
+
expect(testrun).to.nested.include({
|
|
461
|
+
'done.callCount': 1
|
|
462
|
+
});
|
|
463
|
+
testrun.done.getCall(0).args[0] && console.error(testrun.done.getCall(0).args[0].stack);
|
|
464
|
+
expect(testrun.done.getCall(0).args[0]).to.be.null;
|
|
465
|
+
expect(testrun).to.nested.include({
|
|
466
|
+
'start.callCount': 1
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('should have sent the request thrice with failed authentication', function () {
|
|
471
|
+
expect(testrun).to.nested.include({
|
|
472
|
+
'request.callCount': 3
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
var err = testrun.request.thirdCall.args[0],
|
|
476
|
+
response = testrun.request.thirdCall.args[2];
|
|
477
|
+
|
|
478
|
+
expect(err).to.be.null;
|
|
479
|
+
expect(response).to.have.property('code', 401);
|
|
480
|
+
});
|
|
481
|
+
});
|
|
256
482
|
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
var sinon = require('sinon'),
|
|
2
|
+
expect = require('chai').expect,
|
|
3
|
+
createBytesServer = require('../../fixtures/server').createBytesServer;
|
|
4
|
+
|
|
5
|
+
// @todo move to bipbip
|
|
6
|
+
describe('Benchmark: large response', function () {
|
|
7
|
+
var testrun,
|
|
8
|
+
PORT = 5050,
|
|
9
|
+
URL = 'http://localhost:' + PORT,
|
|
10
|
+
server = createBytesServer();
|
|
11
|
+
|
|
12
|
+
before(function (done) {
|
|
13
|
+
server.listen(PORT, done);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
after(function (done) {
|
|
17
|
+
server.destroy(done);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// @todo increase to 100 MB once we drop support for Node v6
|
|
21
|
+
describe('50 MB response with test script', function () {
|
|
22
|
+
const RESPONSE_SIZE = 50 * 1024 * 1024; // 50 MB
|
|
23
|
+
|
|
24
|
+
before(function (done) {
|
|
25
|
+
this.run({
|
|
26
|
+
timeout: {
|
|
27
|
+
global: 20000 // 20s
|
|
28
|
+
},
|
|
29
|
+
collection: {
|
|
30
|
+
item: [{
|
|
31
|
+
request: URL + '/' + (RESPONSE_SIZE),
|
|
32
|
+
event: [{
|
|
33
|
+
listen: 'test',
|
|
34
|
+
script: {
|
|
35
|
+
type: 'text/javascript',
|
|
36
|
+
exec: `
|
|
37
|
+
"use sandbox2";
|
|
38
|
+
pm.test("response size", function () {
|
|
39
|
+
pm.response.to.be.ok;
|
|
40
|
+
pm.response.to.have.statusCode(200);
|
|
41
|
+
pm.expect(pm.response.size().body).to.equal(${RESPONSE_SIZE});
|
|
42
|
+
});
|
|
43
|
+
`
|
|
44
|
+
}
|
|
45
|
+
}]
|
|
46
|
+
}]
|
|
47
|
+
}
|
|
48
|
+
}, function (err, results) {
|
|
49
|
+
testrun = results;
|
|
50
|
+
done(err);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should complete the run', function () {
|
|
55
|
+
expect(testrun).to.be.ok;
|
|
56
|
+
sinon.assert.calledOnce(testrun.start);
|
|
57
|
+
sinon.assert.calledOnce(testrun.done);
|
|
58
|
+
sinon.assert.calledWith(testrun.done.getCall(0), null);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should run the test script successfully', function () {
|
|
62
|
+
sinon.assert.calledOnce(testrun.request);
|
|
63
|
+
sinon.assert.calledWith(testrun.request.getCall(0), null);
|
|
64
|
+
|
|
65
|
+
sinon.assert.calledOnce(testrun.response);
|
|
66
|
+
sinon.assert.calledWith(testrun.response.getCall(0), null);
|
|
67
|
+
|
|
68
|
+
sinon.assert.calledOnce(testrun.assertion);
|
|
69
|
+
|
|
70
|
+
expect(testrun.assertion.getCall(0).args[1][0]).to.include({
|
|
71
|
+
error: null,
|
|
72
|
+
index: 0,
|
|
73
|
+
passed: true,
|
|
74
|
+
skipped: false,
|
|
75
|
+
name: 'response size'
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -5,6 +5,61 @@ var expect = require('chai').expect,
|
|
|
5
5
|
describe('sandbox library - pm api', function () {
|
|
6
6
|
var testrun;
|
|
7
7
|
|
|
8
|
+
describe('sanity', function () {
|
|
9
|
+
before(function (done) {
|
|
10
|
+
this.run({
|
|
11
|
+
collection: {
|
|
12
|
+
item: [{
|
|
13
|
+
request: 'https://postman-echo.com/get',
|
|
14
|
+
event: [{
|
|
15
|
+
listen: 'test',
|
|
16
|
+
script: {
|
|
17
|
+
type: 'text/javascript',
|
|
18
|
+
exec: `
|
|
19
|
+
console.log(pm.request.toJSON());
|
|
20
|
+
console.log(pm.response.toJSON());
|
|
21
|
+
`
|
|
22
|
+
}
|
|
23
|
+
}]
|
|
24
|
+
}]
|
|
25
|
+
}
|
|
26
|
+
}, function (err, results) {
|
|
27
|
+
testrun = results;
|
|
28
|
+
done(err);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should complete the run', function () {
|
|
33
|
+
expect(testrun).to.be.ok;
|
|
34
|
+
sinon.assert.calledOnce(testrun.start);
|
|
35
|
+
sinon.assert.calledOnce(testrun.done);
|
|
36
|
+
sinon.assert.calledWith(testrun.done.getCall(0), null);
|
|
37
|
+
|
|
38
|
+
sinon.assert.calledOnce(testrun.request);
|
|
39
|
+
sinon.assert.calledWith(testrun.request.getCall(0), null);
|
|
40
|
+
|
|
41
|
+
sinon.assert.calledOnce(testrun.response);
|
|
42
|
+
sinon.assert.calledWith(testrun.response.getCall(0), null);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should run the test script successfully', function () {
|
|
46
|
+
var request = testrun.response.getCall(0).args[3],
|
|
47
|
+
response = testrun.response.getCall(0).args[2];
|
|
48
|
+
|
|
49
|
+
sinon.assert.calledOnce(testrun.script);
|
|
50
|
+
sinon.assert.calledWith(testrun.script.getCall(0), null);
|
|
51
|
+
|
|
52
|
+
sinon.assert.calledOnce(testrun.test);
|
|
53
|
+
sinon.assert.calledWith(testrun.script.getCall(0), null);
|
|
54
|
+
|
|
55
|
+
sinon.assert.calledTwice(testrun.console);
|
|
56
|
+
|
|
57
|
+
// validate pm.request and pm.response
|
|
58
|
+
expect(testrun.console.getCall(0).args[2]).to.eql(request.toJSON());
|
|
59
|
+
expect(testrun.console.getCall(1).args[2]).to.eql(response.toJSON());
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
8
63
|
describe('chai', function () {
|
|
9
64
|
before(function (done) {
|
|
10
65
|
this.run({
|
|
@@ -184,4 +184,73 @@ describe('proxy', function () {
|
|
|
184
184
|
proxyServer.destroy();
|
|
185
185
|
});
|
|
186
186
|
});
|
|
187
|
+
|
|
188
|
+
// issue: https://github.com/postmanlabs/postman-app-support/issues/5626
|
|
189
|
+
// Skip in TRAVIS because IPv6 is disabled there
|
|
190
|
+
// eslint-disable-next-line no-process-env
|
|
191
|
+
(process.env.TRAVIS ? describe.skip : describe)('IPv6 request through IPv4 proxy', function () {
|
|
192
|
+
var proxyList = new ProxyConfigList({}, [{
|
|
193
|
+
host: proxyHost,
|
|
194
|
+
port: port
|
|
195
|
+
}]),
|
|
196
|
+
requestPort = 9091,
|
|
197
|
+
requestServer;
|
|
198
|
+
|
|
199
|
+
before(function (done) {
|
|
200
|
+
proxyServer = server.createProxyServer({
|
|
201
|
+
useIPv6: true,
|
|
202
|
+
headers: {'x-postman-proxy': 'true'}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// listening on IPv4
|
|
206
|
+
proxyServer.listen(port, '127.0.0.1');
|
|
207
|
+
|
|
208
|
+
requestServer = server.createHTTPServer();
|
|
209
|
+
requestServer.on('/foo', function (req, res) {
|
|
210
|
+
var proxyHeader = Boolean(req.headers['x-postman-proxy']);
|
|
211
|
+
|
|
212
|
+
res.writeHead(200, {'content-type': 'text/plain'});
|
|
213
|
+
res.end(`Hello Postman!!\nproxy-header:${proxyHeader}`);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// listening on IPv6
|
|
217
|
+
requestServer.listen(requestPort, '::1');
|
|
218
|
+
|
|
219
|
+
this.run({
|
|
220
|
+
collection: {
|
|
221
|
+
item: {
|
|
222
|
+
request: `http://localhost:${requestPort}/foo`
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
proxies: proxyList
|
|
226
|
+
}, function (err, results) {
|
|
227
|
+
testrun = results;
|
|
228
|
+
done(err);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should have started and completed the test run', function () {
|
|
233
|
+
expect(testrun).to.be.ok;
|
|
234
|
+
expect(testrun).to.nested.include({
|
|
235
|
+
'done.calledOnce': true,
|
|
236
|
+
'start.calledOnce': true
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should receive response from the proxy', function () {
|
|
241
|
+
var response = testrun.request.getCall(0).args[2],
|
|
242
|
+
request = testrun.request.getCall(0).args[3];
|
|
243
|
+
|
|
244
|
+
expect(testrun.request.calledOnce).to.be.ok;
|
|
245
|
+
expect(request.proxy.getProxyUrl()).to.eql(proxyUrlForHttpRequest);
|
|
246
|
+
expect(response.reason()).to.eql('OK');
|
|
247
|
+
expect(response.text()).to.include('Hello Postman!!');
|
|
248
|
+
expect(response.text()).to.include('proxy-header:true');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
after(function () {
|
|
252
|
+
proxyServer.destroy();
|
|
253
|
+
requestServer.destroy();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
187
256
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
var _ = require('lodash'),
|
|
2
2
|
expect = require('chai').expect,
|
|
3
|
-
btoa = require('btoa'),
|
|
4
3
|
aws4 = require('aws4'),
|
|
5
4
|
sdk = require('postman-collection'),
|
|
6
5
|
AuthLoader = require('../../lib/authorizer').AuthLoader,
|
|
@@ -91,7 +90,8 @@ describe('Auth Handler:', function () {
|
|
|
91
90
|
authInterface = createAuthInterface(auth),
|
|
92
91
|
username = rawRequests.basic.auth.basic.username,
|
|
93
92
|
password = rawRequests.basic.auth.basic.password,
|
|
94
|
-
expectedAuthHeader = 'Authorization: Basic ' +
|
|
93
|
+
expectedAuthHeader = 'Authorization: Basic ' +
|
|
94
|
+
Buffer.from(`${username}:${password}`, 'utf8').toString('base64'),
|
|
95
95
|
handler = AuthLoader.getHandler(auth.type),
|
|
96
96
|
headers,
|
|
97
97
|
authHeader;
|
|
@@ -106,6 +106,27 @@ describe('Auth Handler:', function () {
|
|
|
106
106
|
expect(authHeader.system).to.be.true;
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
it('should generate correct header for parameters with unicode characters', function () {
|
|
110
|
+
var rawBasicReq = _.cloneDeep(rawRequests.basic),
|
|
111
|
+
request,
|
|
112
|
+
authInterface,
|
|
113
|
+
handler;
|
|
114
|
+
|
|
115
|
+
rawBasicReq.auth.basic = {username: '中文', password: '文中'};
|
|
116
|
+
request = new Request(rawBasicReq);
|
|
117
|
+
authInterface = createAuthInterface(request.auth);
|
|
118
|
+
handler = AuthLoader.getHandler(request.auth.type);
|
|
119
|
+
handler.sign(authInterface, request, _.noop);
|
|
120
|
+
|
|
121
|
+
expect(request.headers.toJSON()).to.eql([
|
|
122
|
+
{
|
|
123
|
+
key: 'Authorization',
|
|
124
|
+
value: 'Basic ' + Buffer.from('中文:文中', 'utf8').toString('base64'),
|
|
125
|
+
system: true
|
|
126
|
+
}
|
|
127
|
+
]);
|
|
128
|
+
});
|
|
129
|
+
|
|
109
130
|
it('should use default values for the missing parameters', function () {
|
|
110
131
|
var rawBasicReq = _.cloneDeep(rawRequests.basic),
|
|
111
132
|
request,
|
|
@@ -119,7 +140,11 @@ describe('Auth Handler:', function () {
|
|
|
119
140
|
handler.sign(authInterface, request, _.noop);
|
|
120
141
|
|
|
121
142
|
expect(request.headers.toJSON()).to.eql([
|
|
122
|
-
{
|
|
143
|
+
{
|
|
144
|
+
key: 'Authorization',
|
|
145
|
+
value: 'Basic ' + Buffer.from('foo:', 'utf8').toString('base64'),
|
|
146
|
+
system: true
|
|
147
|
+
}
|
|
123
148
|
]);
|
|
124
149
|
|
|
125
150
|
rawBasicReq.auth.basic = {password: 'foo'}; // no username present
|
|
@@ -129,7 +154,11 @@ describe('Auth Handler:', function () {
|
|
|
129
154
|
handler.sign(authInterface, request, _.noop);
|
|
130
155
|
|
|
131
156
|
expect(request.headers.toJSON()).to.eql([
|
|
132
|
-
{
|
|
157
|
+
{
|
|
158
|
+
key: 'Authorization',
|
|
159
|
+
value: 'Basic ' + Buffer.from(':foo', 'utf8').toString('base64'),
|
|
160
|
+
system: true
|
|
161
|
+
}
|
|
133
162
|
]);
|
|
134
163
|
|
|
135
164
|
rawBasicReq.auth.basic = {}; // no username and no password present
|
|
@@ -139,7 +168,11 @@ describe('Auth Handler:', function () {
|
|
|
139
168
|
handler.sign(authInterface, request, _.noop);
|
|
140
169
|
|
|
141
170
|
expect(request.headers.toJSON()).to.eql([
|
|
142
|
-
{
|
|
171
|
+
{
|
|
172
|
+
key: 'Authorization',
|
|
173
|
+
value: 'Basic ' + Buffer.from(':', 'utf8').toString('base64'),
|
|
174
|
+
system: true
|
|
175
|
+
}
|
|
143
176
|
]);
|
|
144
177
|
});
|
|
145
178
|
});
|
|
@@ -698,7 +731,7 @@ describe('Auth Handler:', function () {
|
|
|
698
731
|
});
|
|
699
732
|
});
|
|
700
733
|
|
|
701
|
-
it('should
|
|
734
|
+
it('should treat unknown token type as "Bearer"', function () {
|
|
702
735
|
var clonedRequestObj,
|
|
703
736
|
request,
|
|
704
737
|
auth,
|
|
@@ -715,6 +748,31 @@ describe('Auth Handler:', function () {
|
|
|
715
748
|
|
|
716
749
|
handler.sign(authInterface, request, _.noop);
|
|
717
750
|
|
|
751
|
+
expect(request.headers.all()).to.be.an('array').that.has.lengthOf(1);
|
|
752
|
+
expect(request.headers.toJSON()[0]).to.eql({
|
|
753
|
+
key: 'Authorization',
|
|
754
|
+
value: 'Bearer ' + requestObj.auth.oauth2.accessToken,
|
|
755
|
+
system: true
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
it('should return when token type is MAC', function () {
|
|
760
|
+
var clonedRequestObj,
|
|
761
|
+
request,
|
|
762
|
+
auth,
|
|
763
|
+
authInterface,
|
|
764
|
+
handler;
|
|
765
|
+
|
|
766
|
+
clonedRequestObj = _.cloneDeep(requestObj);
|
|
767
|
+
clonedRequestObj.auth.oauth2.tokenType = 'mac';
|
|
768
|
+
|
|
769
|
+
request = new Request(clonedRequestObj);
|
|
770
|
+
auth = request.auth;
|
|
771
|
+
authInterface = createAuthInterface(auth);
|
|
772
|
+
handler = AuthLoader.getHandler(auth.type);
|
|
773
|
+
|
|
774
|
+
handler.sign(authInterface, request, _.noop);
|
|
775
|
+
|
|
718
776
|
expect(request.headers.all()).to.be.an('array').that.is.empty;
|
|
719
777
|
expect(request.url.query.all()).to.be.an('array').that.is.empty;
|
|
720
778
|
});
|
|
@@ -827,37 +885,6 @@ describe('Auth Handler:', function () {
|
|
|
827
885
|
query: [{key: 'access_token', value: 'old-token'}],
|
|
828
886
|
variable: []
|
|
829
887
|
});
|
|
830
|
-
|
|
831
|
-
// invalid token type
|
|
832
|
-
requestWithAuthHeader = _.defaults({
|
|
833
|
-
auth: {
|
|
834
|
-
type: 'oauth2',
|
|
835
|
-
oauth2: {
|
|
836
|
-
accessToken: '123456789abcdefghi',
|
|
837
|
-
addTokenTo: 'queryParams',
|
|
838
|
-
tokenType: 'micdrop'
|
|
839
|
-
}
|
|
840
|
-
},
|
|
841
|
-
header: [{key: 'Authorization', value: 'Old-Header'}],
|
|
842
|
-
url: 'https://postman-echo.com/get?access_token=old-token'
|
|
843
|
-
}, requestObj);
|
|
844
|
-
|
|
845
|
-
request = new Request(requestWithAuthHeader);
|
|
846
|
-
auth = request.auth;
|
|
847
|
-
authInterface = createAuthInterface(auth);
|
|
848
|
-
handler = AuthLoader.getHandler(auth.type);
|
|
849
|
-
|
|
850
|
-
handler.sign(authInterface, request, _.noop);
|
|
851
|
-
|
|
852
|
-
expect(request.headers.toJSON()).to.eql([{key: 'Authorization', value: 'Old-Header'}]);
|
|
853
|
-
|
|
854
|
-
expect(request.url.toJSON()).to.eql({
|
|
855
|
-
protocol: 'https',
|
|
856
|
-
path: ['get'],
|
|
857
|
-
host: ['postman-echo', 'com'],
|
|
858
|
-
query: [{key: 'access_token', value: 'old-token'}],
|
|
859
|
-
variable: []
|
|
860
|
-
});
|
|
861
888
|
});
|
|
862
889
|
});
|
|
863
890
|
|