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/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
@@ -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