request-easy-validator 1.0.5
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 +55 -0
- package/README.md +1133 -0
- package/index.js +200 -0
- package/lib/auth.js +167 -0
- package/lib/callers.js +32 -0
- package/lib/constants.js +28 -0
- package/lib/cookies.js +38 -0
- package/lib/getProxyFromURI.js +79 -0
- package/lib/har.js +205 -0
- package/lib/hawk.js +89 -0
- package/lib/helpers.js +66 -0
- package/lib/multipart.js +112 -0
- package/lib/oauth.js +148 -0
- package/lib/querystring.js +50 -0
- package/lib/redirect.js +154 -0
- package/lib/tunnel.js +175 -0
- package/package.json +84 -0
- package/request.js +1553 -0
package/lib/har.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var fs = require('fs')
|
|
4
|
+
var qs = require('querystring')
|
|
5
|
+
var validate = require('har-validator')
|
|
6
|
+
var extend = require('extend')
|
|
7
|
+
|
|
8
|
+
function Har (request) {
|
|
9
|
+
this.request = request
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
Har.prototype.reducer = function (obj, pair) {
|
|
13
|
+
// new property ?
|
|
14
|
+
if (obj[pair.name] === undefined) {
|
|
15
|
+
obj[pair.name] = pair.value
|
|
16
|
+
return obj
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// existing? convert to array
|
|
20
|
+
var arr = [
|
|
21
|
+
obj[pair.name],
|
|
22
|
+
pair.value
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
obj[pair.name] = arr
|
|
26
|
+
|
|
27
|
+
return obj
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Har.prototype.prep = function (data) {
|
|
31
|
+
// construct utility properties
|
|
32
|
+
data.queryObj = {}
|
|
33
|
+
data.headersObj = {}
|
|
34
|
+
data.postData.jsonObj = false
|
|
35
|
+
data.postData.paramsObj = false
|
|
36
|
+
|
|
37
|
+
// construct query objects
|
|
38
|
+
if (data.queryString && data.queryString.length) {
|
|
39
|
+
data.queryObj = data.queryString.reduce(this.reducer, {})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// construct headers objects
|
|
43
|
+
if (data.headers && data.headers.length) {
|
|
44
|
+
// loweCase header keys
|
|
45
|
+
data.headersObj = data.headers.reduceRight(function (headers, header) {
|
|
46
|
+
headers[header.name] = header.value
|
|
47
|
+
return headers
|
|
48
|
+
}, {})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// construct Cookie header
|
|
52
|
+
if (data.cookies && data.cookies.length) {
|
|
53
|
+
var cookies = data.cookies.map(function (cookie) {
|
|
54
|
+
return cookie.name + '=' + cookie.value
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (cookies.length) {
|
|
58
|
+
data.headersObj.cookie = cookies.join('; ')
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// prep body
|
|
63
|
+
function some (arr) {
|
|
64
|
+
return arr.some(function (type) {
|
|
65
|
+
return data.postData.mimeType.indexOf(type) === 0
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (some([
|
|
70
|
+
'multipart/mixed',
|
|
71
|
+
'multipart/related',
|
|
72
|
+
'multipart/form-data',
|
|
73
|
+
'multipart/alternative'])) {
|
|
74
|
+
// reset values
|
|
75
|
+
data.postData.mimeType = 'multipart/form-data'
|
|
76
|
+
} else if (some([
|
|
77
|
+
'application/x-www-form-urlencoded'])) {
|
|
78
|
+
if (!data.postData.params) {
|
|
79
|
+
data.postData.text = ''
|
|
80
|
+
} else {
|
|
81
|
+
data.postData.paramsObj = data.postData.params.reduce(this.reducer, {})
|
|
82
|
+
|
|
83
|
+
// always overwrite
|
|
84
|
+
data.postData.text = qs.stringify(data.postData.paramsObj)
|
|
85
|
+
}
|
|
86
|
+
} else if (some([
|
|
87
|
+
'text/json',
|
|
88
|
+
'text/x-json',
|
|
89
|
+
'application/json',
|
|
90
|
+
'application/x-json'])) {
|
|
91
|
+
data.postData.mimeType = 'application/json'
|
|
92
|
+
|
|
93
|
+
if (data.postData.text) {
|
|
94
|
+
try {
|
|
95
|
+
data.postData.jsonObj = JSON.parse(data.postData.text)
|
|
96
|
+
} catch (e) {
|
|
97
|
+
this.request.debug(e)
|
|
98
|
+
|
|
99
|
+
// force back to text/plain
|
|
100
|
+
data.postData.mimeType = 'text/plain'
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return data
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Har.prototype.options = function (options) {
|
|
109
|
+
// skip if no har property defined
|
|
110
|
+
if (!options.har) {
|
|
111
|
+
return options
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var har = {}
|
|
115
|
+
extend(har, options.har)
|
|
116
|
+
|
|
117
|
+
// only process the first entry
|
|
118
|
+
if (har.log && har.log.entries) {
|
|
119
|
+
har = har.log.entries[0]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// add optional properties to make validation successful
|
|
123
|
+
har.url = har.url || options.url || options.uri || options.baseUrl || '/'
|
|
124
|
+
har.httpVersion = har.httpVersion || 'HTTP/1.1'
|
|
125
|
+
har.queryString = har.queryString || []
|
|
126
|
+
har.headers = har.headers || []
|
|
127
|
+
har.cookies = har.cookies || []
|
|
128
|
+
har.postData = har.postData || {}
|
|
129
|
+
har.postData.mimeType = har.postData.mimeType || 'application/octet-stream'
|
|
130
|
+
|
|
131
|
+
har.bodySize = 0
|
|
132
|
+
har.headersSize = 0
|
|
133
|
+
har.postData.size = 0
|
|
134
|
+
|
|
135
|
+
if (!validate.request(har)) {
|
|
136
|
+
return options
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// clean up and get some utility properties
|
|
140
|
+
var req = this.prep(har)
|
|
141
|
+
|
|
142
|
+
// construct new options
|
|
143
|
+
if (req.url) {
|
|
144
|
+
options.url = req.url
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (req.method) {
|
|
148
|
+
options.method = req.method
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (Object.keys(req.queryObj).length) {
|
|
152
|
+
options.qs = req.queryObj
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (Object.keys(req.headersObj).length) {
|
|
156
|
+
options.headers = req.headersObj
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function test (type) {
|
|
160
|
+
return req.postData.mimeType.indexOf(type) === 0
|
|
161
|
+
}
|
|
162
|
+
if (test('application/x-www-form-urlencoded')) {
|
|
163
|
+
options.form = req.postData.paramsObj
|
|
164
|
+
} else if (test('application/json')) {
|
|
165
|
+
if (req.postData.jsonObj) {
|
|
166
|
+
options.body = req.postData.jsonObj
|
|
167
|
+
options.json = true
|
|
168
|
+
}
|
|
169
|
+
} else if (test('multipart/form-data')) {
|
|
170
|
+
options.formData = {}
|
|
171
|
+
|
|
172
|
+
req.postData.params.forEach(function (param) {
|
|
173
|
+
var attachment = {}
|
|
174
|
+
|
|
175
|
+
if (!param.fileName && !param.contentType) {
|
|
176
|
+
options.formData[param.name] = param.value
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// attempt to read from disk!
|
|
181
|
+
if (param.fileName && !param.value) {
|
|
182
|
+
attachment.value = fs.createReadStream(param.fileName)
|
|
183
|
+
} else if (param.value) {
|
|
184
|
+
attachment.value = param.value
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (param.fileName) {
|
|
188
|
+
attachment.options = {
|
|
189
|
+
filename: param.fileName,
|
|
190
|
+
contentType: param.contentType ? param.contentType : null
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
options.formData[param.name] = attachment
|
|
195
|
+
})
|
|
196
|
+
} else {
|
|
197
|
+
if (req.postData.text) {
|
|
198
|
+
options.body = req.postData.text
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return options
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
exports.Har = Har
|
package/lib/hawk.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto')
|
|
4
|
+
|
|
5
|
+
function randomString (size) {
|
|
6
|
+
var bits = (size + 1) * 6
|
|
7
|
+
var buffer = crypto.randomBytes(Math.ceil(bits / 8))
|
|
8
|
+
var string = buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
|
9
|
+
return string.slice(0, size)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function calculatePayloadHash (payload, algorithm, contentType) {
|
|
13
|
+
var hash = crypto.createHash(algorithm)
|
|
14
|
+
hash.update('hawk.1.payload\n')
|
|
15
|
+
hash.update((contentType ? contentType.split(';')[0].trim().toLowerCase() : '') + '\n')
|
|
16
|
+
hash.update(payload || '')
|
|
17
|
+
hash.update('\n')
|
|
18
|
+
return hash.digest('base64')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
exports.calculateMac = function (credentials, opts) {
|
|
22
|
+
var normalized = 'hawk.1.header\n' +
|
|
23
|
+
opts.ts + '\n' +
|
|
24
|
+
opts.nonce + '\n' +
|
|
25
|
+
(opts.method || '').toUpperCase() + '\n' +
|
|
26
|
+
opts.resource + '\n' +
|
|
27
|
+
opts.host.toLowerCase() + '\n' +
|
|
28
|
+
opts.port + '\n' +
|
|
29
|
+
(opts.hash || '') + '\n'
|
|
30
|
+
|
|
31
|
+
if (opts.ext) {
|
|
32
|
+
normalized = normalized + opts.ext.replace('\\', '\\\\').replace('\n', '\\n')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
normalized = normalized + '\n'
|
|
36
|
+
|
|
37
|
+
if (opts.app) {
|
|
38
|
+
normalized = normalized + opts.app + '\n' + (opts.dlg || '') + '\n'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var hmac = crypto.createHmac(credentials.algorithm, credentials.key).update(normalized)
|
|
42
|
+
var digest = hmac.digest('base64')
|
|
43
|
+
return digest
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
exports.header = function (uri, method, opts) {
|
|
47
|
+
var timestamp = opts.timestamp || Math.floor((Date.now() + (opts.localtimeOffsetMsec || 0)) / 1000)
|
|
48
|
+
var credentials = opts.credentials
|
|
49
|
+
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
|
|
50
|
+
return ''
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (['sha1', 'sha256'].indexOf(credentials.algorithm) === -1) {
|
|
54
|
+
return ''
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
var artifacts = {
|
|
58
|
+
ts: timestamp,
|
|
59
|
+
nonce: opts.nonce || randomString(6),
|
|
60
|
+
method: method,
|
|
61
|
+
resource: uri.pathname + (uri.search || ''),
|
|
62
|
+
host: uri.hostname,
|
|
63
|
+
port: uri.port || (uri.protocol === 'http:' ? 80 : 443),
|
|
64
|
+
hash: opts.hash,
|
|
65
|
+
ext: opts.ext,
|
|
66
|
+
app: opts.app,
|
|
67
|
+
dlg: opts.dlg
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!artifacts.hash && (opts.payload || opts.payload === '')) {
|
|
71
|
+
artifacts.hash = calculatePayloadHash(opts.payload, credentials.algorithm, opts.contentType)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var mac = exports.calculateMac(credentials, artifacts)
|
|
75
|
+
|
|
76
|
+
var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''
|
|
77
|
+
var header = 'Hawk id="' + credentials.id +
|
|
78
|
+
'", ts="' + artifacts.ts +
|
|
79
|
+
'", nonce="' + artifacts.nonce +
|
|
80
|
+
(artifacts.hash ? '", hash="' + artifacts.hash : '') +
|
|
81
|
+
(hasExt ? '", ext="' + artifacts.ext.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : '') +
|
|
82
|
+
'", mac="' + mac + '"'
|
|
83
|
+
|
|
84
|
+
if (artifacts.app) {
|
|
85
|
+
header = header + ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"'
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return header
|
|
89
|
+
}
|
package/lib/helpers.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var jsonSafeStringify = require('json-stringify-safe')
|
|
4
|
+
var crypto = require('crypto')
|
|
5
|
+
var Buffer = require('safe-buffer').Buffer
|
|
6
|
+
|
|
7
|
+
var defer = typeof setImmediate === 'undefined'
|
|
8
|
+
? process.nextTick
|
|
9
|
+
: setImmediate
|
|
10
|
+
|
|
11
|
+
function paramsHaveRequestBody (params) {
|
|
12
|
+
return (
|
|
13
|
+
params.body ||
|
|
14
|
+
params.requestBodyStream ||
|
|
15
|
+
(params.json && typeof params.json !== 'boolean') ||
|
|
16
|
+
params.multipart
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function safeStringify (obj, replacer) {
|
|
21
|
+
var ret
|
|
22
|
+
try {
|
|
23
|
+
ret = JSON.stringify(obj, replacer)
|
|
24
|
+
} catch (e) {
|
|
25
|
+
ret = jsonSafeStringify(obj, replacer)
|
|
26
|
+
}
|
|
27
|
+
return ret
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function md5 (str) {
|
|
31
|
+
return crypto.createHash('md5').update(str).digest('hex')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isReadStream (rs) {
|
|
35
|
+
return rs.readable && rs.path && rs.mode
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toBase64 (str) {
|
|
39
|
+
return Buffer.from(str || '', 'utf8').toString('base64')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function copy (obj) {
|
|
43
|
+
var o = {}
|
|
44
|
+
Object.keys(obj).forEach(function (i) {
|
|
45
|
+
o[i] = obj[i]
|
|
46
|
+
})
|
|
47
|
+
return o
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function version () {
|
|
51
|
+
var numbers = process.version.replace('v', '').split('.')
|
|
52
|
+
return {
|
|
53
|
+
major: parseInt(numbers[0], 10),
|
|
54
|
+
minor: parseInt(numbers[1], 10),
|
|
55
|
+
patch: parseInt(numbers[2], 10)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
exports.paramsHaveRequestBody = paramsHaveRequestBody
|
|
60
|
+
exports.safeStringify = safeStringify
|
|
61
|
+
exports.md5 = md5
|
|
62
|
+
exports.isReadStream = isReadStream
|
|
63
|
+
exports.toBase64 = toBase64
|
|
64
|
+
exports.copy = copy
|
|
65
|
+
exports.version = version
|
|
66
|
+
exports.defer = defer
|
package/lib/multipart.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var uuid = require('uuid/v4')
|
|
4
|
+
var CombinedStream = require('combined-stream')
|
|
5
|
+
var isstream = require('isstream')
|
|
6
|
+
var Buffer = require('safe-buffer').Buffer
|
|
7
|
+
|
|
8
|
+
function Multipart (request) {
|
|
9
|
+
this.request = request
|
|
10
|
+
this.boundary = uuid()
|
|
11
|
+
this.chunked = false
|
|
12
|
+
this.body = null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Multipart.prototype.isChunked = function (options) {
|
|
16
|
+
var self = this
|
|
17
|
+
var chunked = false
|
|
18
|
+
var parts = options.data || options
|
|
19
|
+
|
|
20
|
+
if (!parts.forEach) {
|
|
21
|
+
self.request.emit('error', new Error('Argument error, options.multipart.'))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (options.chunked !== undefined) {
|
|
25
|
+
chunked = options.chunked
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (self.request.getHeader('transfer-encoding') === 'chunked') {
|
|
29
|
+
chunked = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!chunked) {
|
|
33
|
+
parts.forEach(function (part) {
|
|
34
|
+
if (typeof part.body === 'undefined') {
|
|
35
|
+
self.request.emit('error', new Error('Body attribute missing in multipart.'))
|
|
36
|
+
}
|
|
37
|
+
if (isstream(part.body)) {
|
|
38
|
+
chunked = true
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return chunked
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Multipart.prototype.setHeaders = function (chunked) {
|
|
47
|
+
var self = this
|
|
48
|
+
|
|
49
|
+
if (chunked && !self.request.hasHeader('transfer-encoding')) {
|
|
50
|
+
self.request.setHeader('transfer-encoding', 'chunked')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var header = self.request.getHeader('content-type')
|
|
54
|
+
|
|
55
|
+
if (!header || header.indexOf('multipart') === -1) {
|
|
56
|
+
self.request.setHeader('content-type', 'multipart/related; boundary=' + self.boundary)
|
|
57
|
+
} else {
|
|
58
|
+
if (header.indexOf('boundary') !== -1) {
|
|
59
|
+
self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1')
|
|
60
|
+
} else {
|
|
61
|
+
self.request.setHeader('content-type', header + '; boundary=' + self.boundary)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Multipart.prototype.build = function (parts, chunked) {
|
|
67
|
+
var self = this
|
|
68
|
+
var body = chunked ? new CombinedStream() : []
|
|
69
|
+
|
|
70
|
+
function add (part) {
|
|
71
|
+
if (typeof part === 'number') {
|
|
72
|
+
part = part.toString()
|
|
73
|
+
}
|
|
74
|
+
return chunked ? body.append(part) : body.push(Buffer.from(part))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (self.request.preambleCRLF) {
|
|
78
|
+
add('\r\n')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
parts.forEach(function (part) {
|
|
82
|
+
var preamble = '--' + self.boundary + '\r\n'
|
|
83
|
+
Object.keys(part).forEach(function (key) {
|
|
84
|
+
if (key === 'body') { return }
|
|
85
|
+
preamble += key + ': ' + part[key] + '\r\n'
|
|
86
|
+
})
|
|
87
|
+
preamble += '\r\n'
|
|
88
|
+
add(preamble)
|
|
89
|
+
add(part.body)
|
|
90
|
+
add('\r\n')
|
|
91
|
+
})
|
|
92
|
+
add('--' + self.boundary + '--')
|
|
93
|
+
|
|
94
|
+
if (self.request.postambleCRLF) {
|
|
95
|
+
add('\r\n')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return body
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Multipart.prototype.onRequest = function (options) {
|
|
102
|
+
var self = this
|
|
103
|
+
|
|
104
|
+
var chunked = self.isChunked(options)
|
|
105
|
+
var parts = options.data || options
|
|
106
|
+
|
|
107
|
+
self.setHeaders(chunked)
|
|
108
|
+
self.chunked = chunked
|
|
109
|
+
self.body = self.build(parts, chunked)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
exports.Multipart = Multipart
|
package/lib/oauth.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var url = require('url')
|
|
4
|
+
var qs = require('qs')
|
|
5
|
+
var caseless = require('caseless')
|
|
6
|
+
var uuid = require('uuid/v4')
|
|
7
|
+
var oauth = require('oauth-sign')
|
|
8
|
+
var crypto = require('crypto')
|
|
9
|
+
var Buffer = require('safe-buffer').Buffer
|
|
10
|
+
|
|
11
|
+
function OAuth (request) {
|
|
12
|
+
this.request = request
|
|
13
|
+
this.params = null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
OAuth.prototype.buildParams = function (_oauth, uri, method, query, form, qsLib) {
|
|
17
|
+
var oa = {}
|
|
18
|
+
for (var i in _oauth) {
|
|
19
|
+
oa['oauth_' + i] = _oauth[i]
|
|
20
|
+
}
|
|
21
|
+
if (!oa.oauth_version) {
|
|
22
|
+
oa.oauth_version = '1.0'
|
|
23
|
+
}
|
|
24
|
+
if (!oa.oauth_timestamp) {
|
|
25
|
+
oa.oauth_timestamp = Math.floor(Date.now() / 1000).toString()
|
|
26
|
+
}
|
|
27
|
+
if (!oa.oauth_nonce) {
|
|
28
|
+
oa.oauth_nonce = uuid().replace(/-/g, '')
|
|
29
|
+
}
|
|
30
|
+
if (!oa.oauth_signature_method) {
|
|
31
|
+
oa.oauth_signature_method = 'HMAC-SHA1'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var consumer_secret_or_private_key = oa.oauth_consumer_secret || oa.oauth_private_key // eslint-disable-line camelcase
|
|
35
|
+
delete oa.oauth_consumer_secret
|
|
36
|
+
delete oa.oauth_private_key
|
|
37
|
+
|
|
38
|
+
var token_secret = oa.oauth_token_secret // eslint-disable-line camelcase
|
|
39
|
+
delete oa.oauth_token_secret
|
|
40
|
+
|
|
41
|
+
var realm = oa.oauth_realm
|
|
42
|
+
delete oa.oauth_realm
|
|
43
|
+
delete oa.oauth_transport_method
|
|
44
|
+
|
|
45
|
+
var baseurl = uri.protocol + '//' + uri.host + uri.pathname
|
|
46
|
+
var params = qsLib.parse([].concat(query, form, qsLib.stringify(oa)).join('&'))
|
|
47
|
+
|
|
48
|
+
oa.oauth_signature = oauth.sign(
|
|
49
|
+
oa.oauth_signature_method,
|
|
50
|
+
method,
|
|
51
|
+
baseurl,
|
|
52
|
+
params,
|
|
53
|
+
consumer_secret_or_private_key, // eslint-disable-line camelcase
|
|
54
|
+
token_secret // eslint-disable-line camelcase
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if (realm) {
|
|
58
|
+
oa.realm = realm
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return oa
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
OAuth.prototype.buildBodyHash = function (_oauth, body) {
|
|
65
|
+
if (['HMAC-SHA1', 'RSA-SHA1'].indexOf(_oauth.signature_method || 'HMAC-SHA1') < 0) {
|
|
66
|
+
this.request.emit('error', new Error('oauth: ' + _oauth.signature_method +
|
|
67
|
+
' signature_method not supported with body_hash signing.'))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var shasum = crypto.createHash('sha1')
|
|
71
|
+
shasum.update(body || '')
|
|
72
|
+
var sha1 = shasum.digest('hex')
|
|
73
|
+
|
|
74
|
+
return Buffer.from(sha1, 'hex').toString('base64')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
OAuth.prototype.concatParams = function (oa, sep, wrap) {
|
|
78
|
+
wrap = wrap || ''
|
|
79
|
+
|
|
80
|
+
var params = Object.keys(oa).filter(function (i) {
|
|
81
|
+
return i !== 'realm' && i !== 'oauth_signature'
|
|
82
|
+
}).sort()
|
|
83
|
+
|
|
84
|
+
if (oa.realm) {
|
|
85
|
+
params.splice(0, 0, 'realm')
|
|
86
|
+
}
|
|
87
|
+
params.push('oauth_signature')
|
|
88
|
+
|
|
89
|
+
return params.map(function (i) {
|
|
90
|
+
return i + '=' + wrap + oauth.rfc3986(oa[i]) + wrap
|
|
91
|
+
}).join(sep)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
OAuth.prototype.onRequest = function (_oauth) {
|
|
95
|
+
var self = this
|
|
96
|
+
self.params = _oauth
|
|
97
|
+
|
|
98
|
+
var uri = self.request.uri || {}
|
|
99
|
+
var method = self.request.method || ''
|
|
100
|
+
var headers = caseless(self.request.headers)
|
|
101
|
+
var body = self.request.body || ''
|
|
102
|
+
var qsLib = self.request.qsLib || qs
|
|
103
|
+
|
|
104
|
+
var form
|
|
105
|
+
var query
|
|
106
|
+
var contentType = headers.get('content-type') || ''
|
|
107
|
+
var formContentType = 'application/x-www-form-urlencoded'
|
|
108
|
+
var transport = _oauth.transport_method || 'header'
|
|
109
|
+
|
|
110
|
+
if (contentType.slice(0, formContentType.length) === formContentType) {
|
|
111
|
+
contentType = formContentType
|
|
112
|
+
form = body
|
|
113
|
+
}
|
|
114
|
+
if (uri.query) {
|
|
115
|
+
query = uri.query
|
|
116
|
+
}
|
|
117
|
+
if (transport === 'body' && (method !== 'POST' || contentType !== formContentType)) {
|
|
118
|
+
self.request.emit('error', new Error('oauth: transport_method of body requires POST ' +
|
|
119
|
+
'and content-type ' + formContentType))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!form && typeof _oauth.body_hash === 'boolean') {
|
|
123
|
+
_oauth.body_hash = self.buildBodyHash(_oauth, self.request.body.toString())
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
var oa = self.buildParams(_oauth, uri, method, query, form, qsLib)
|
|
127
|
+
|
|
128
|
+
switch (transport) {
|
|
129
|
+
case 'header':
|
|
130
|
+
self.request.setHeader('Authorization', 'OAuth ' + self.concatParams(oa, ',', '"'))
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
case 'query':
|
|
134
|
+
var href = self.request.uri.href += (query ? '&' : '?') + self.concatParams(oa, '&')
|
|
135
|
+
self.request.uri = url.parse(href)
|
|
136
|
+
self.request.path = self.request.uri.path
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
case 'body':
|
|
140
|
+
self.request.body = (form ? form + '&' : '') + self.concatParams(oa, '&')
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
default:
|
|
144
|
+
self.request.emit('error', new Error('oauth: transport_method invalid'))
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
exports.OAuth = OAuth
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var qs = require('qs')
|
|
4
|
+
var querystring = require('querystring')
|
|
5
|
+
|
|
6
|
+
function Querystring (request) {
|
|
7
|
+
this.request = request
|
|
8
|
+
this.lib = null
|
|
9
|
+
this.useQuerystring = null
|
|
10
|
+
this.parseOptions = null
|
|
11
|
+
this.stringifyOptions = null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Querystring.prototype.init = function (options) {
|
|
15
|
+
if (this.lib) { return }
|
|
16
|
+
|
|
17
|
+
this.useQuerystring = options.useQuerystring
|
|
18
|
+
this.lib = (this.useQuerystring ? querystring : qs)
|
|
19
|
+
|
|
20
|
+
this.parseOptions = options.qsParseOptions || {}
|
|
21
|
+
this.stringifyOptions = options.qsStringifyOptions || {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Querystring.prototype.stringify = function (obj) {
|
|
25
|
+
return (this.useQuerystring)
|
|
26
|
+
? this.rfc3986(this.lib.stringify(obj,
|
|
27
|
+
this.stringifyOptions.sep || null,
|
|
28
|
+
this.stringifyOptions.eq || null,
|
|
29
|
+
this.stringifyOptions))
|
|
30
|
+
: this.lib.stringify(obj, this.stringifyOptions)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Querystring.prototype.parse = function (str) {
|
|
34
|
+
return (this.useQuerystring)
|
|
35
|
+
? this.lib.parse(str,
|
|
36
|
+
this.parseOptions.sep || null,
|
|
37
|
+
this.parseOptions.eq || null,
|
|
38
|
+
this.parseOptions)
|
|
39
|
+
: this.lib.parse(str, this.parseOptions)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Querystring.prototype.rfc3986 = function (str) {
|
|
43
|
+
return str.replace(/[!'()*]/g, function (c) {
|
|
44
|
+
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Querystring.prototype.unescape = querystring.unescape
|
|
49
|
+
|
|
50
|
+
exports.Querystring = Querystring
|