Something went wrong. Try again.
A Twitter-Bot that is going to increase your followers.
Something went wrong. Try again.
11 kB · 345 lines
JavaScript
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346var aws4 = exports, url = require('url'), querystring = require('querystring'), crypto = require('crypto'), lru = require('./lru'), credentialsCache = lru(1000)
// http://docs.amazonwebservices.com/general/latest/gr/signature-version-4.html
function hmac(key, string, encoding) { return crypto.createHmac('sha256', key).update(string, 'utf8').digest(encoding)}
function hash(string, encoding) { return crypto.createHash('sha256').update(string, 'utf8').digest(encoding)}
// This function assumes the string has already been percent encodedfunction encodeRfc3986(urlEncodedString) { return urlEncodedString.replace(/[!'()*]/g, function(c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase() })}
function encodeRfc3986Full(str) { return encodeRfc3986(encodeURIComponent(str))}
// request: { path | body, [host], [method], [headers], [service], [region] }// credentials: { accessKeyId, secretAccessKey, [sessionToken] }function RequestSigner(request, credentials) {
if (typeof request === 'string') request = url.parse(request)
var headers = request.headers = (request.headers || {}), hostParts = this.matchHost(request.hostname || request.host || headers.Host || headers.host)
this.request = request this.credentials = credentials || this.defaultCredentials()
this.service = request.service || hostParts[0] || '' this.region = request.region || hostParts[1] || 'us-east-1'
// SES uses a different domain from the service name if (this.service === 'email') this.service = 'ses'
if (!request.method && request.body) request.method = 'POST'
if (!headers.Host && !headers.host) { headers.Host = request.hostname || request.host || this.createHost()
// If a port is specified explicitly, use it as is if (request.port) headers.Host += ':' + request.port } if (!request.hostname && !request.host) request.hostname = headers.Host || headers.host
this.isCodeCommitGit = this.service === 'codecommit' && request.method === 'GIT'}
RequestSigner.prototype.matchHost = function(host) { var match = (host || '').match(/([^\.]+)\.(?:([^\.]*)\.)?amazonaws\.com(\.cn)?$/) var hostParts = (match || []).slice(1, 3)
// ES's hostParts are sometimes the other way round, if the value that is expected // to be region equals ‘es’ switch them back // e.g. search-cluster-name-aaaa00aaaa0aaa0aaaaaaa0aaa.us-east-1.es.amazonaws.com if (hostParts[1] === 'es') hostParts = hostParts.reverse()
return hostParts}
// http://docs.aws.amazon.com/general/latest/gr/rande.htmlRequestSigner.prototype.isSingleRegion = function() { // Special case for S3 and SimpleDB in us-east-1 if (['s3', 'sdb'].indexOf(this.service) >= 0 && this.region === 'us-east-1') return true
return ['cloudfront', 'ls', 'route53', 'iam', 'importexport', 'sts'] .indexOf(this.service) >= 0}
RequestSigner.prototype.createHost = function() { var region = this.isSingleRegion() ? '' : (this.service === 's3' && this.region !== 'us-east-1' ? '-' : '.') + this.region, service = this.service === 'ses' ? 'email' : this.service return service + region + '.amazonaws.com'}
RequestSigner.prototype.prepareRequest = function() { this.parsePath()
var request = this.request, headers = request.headers, query
if (request.signQuery) {
this.parsedPath.query = query = this.parsedPath.query || {}
if (this.credentials.sessionToken) query['X-Amz-Security-Token'] = this.credentials.sessionToken
if (this.service === 's3' && !query['X-Amz-Expires']) query['X-Amz-Expires'] = 86400
if (query['X-Amz-Date']) this.datetime = query['X-Amz-Date'] else query['X-Amz-Date'] = this.getDateTime()
query['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256' query['X-Amz-Credential'] = this.credentials.accessKeyId + '/' + this.credentialString() query['X-Amz-SignedHeaders'] = this.signedHeaders()
} else {
if (!request.doNotModifyHeaders && !this.isCodeCommitGit) { if (request.body && !headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
if (request.body && !headers['Content-Length'] && !headers['content-length']) headers['Content-Length'] = Buffer.byteLength(request.body)
if (this.credentials.sessionToken && !headers['X-Amz-Security-Token'] && !headers['x-amz-security-token']) headers['X-Amz-Security-Token'] = this.credentials.sessionToken
if (this.service === 's3' && !headers['X-Amz-Content-Sha256'] && !headers['x-amz-content-sha256']) headers['X-Amz-Content-Sha256'] = hash(this.request.body || '', 'hex')
if (headers['X-Amz-Date'] || headers['x-amz-date']) this.datetime = headers['X-Amz-Date'] || headers['x-amz-date'] else headers['X-Amz-Date'] = this.getDateTime() }
delete headers.Authorization delete headers.authorization }}
RequestSigner.prototype.sign = function() { if (!this.parsedPath) this.prepareRequest()
if (this.request.signQuery) { this.parsedPath.query['X-Amz-Signature'] = this.signature() } else { this.request.headers.Authorization = this.authHeader() }
this.request.path = this.formatPath()
return this.request}
RequestSigner.prototype.getDateTime = function() { if (!this.datetime) { var headers = this.request.headers, date = new Date(headers.Date || headers.date || new Date)
this.datetime = date.toISOString().replace(/[:\-]|\.\d{3}/g, '')
// Remove the trailing 'Z' on the timestamp string for CodeCommit git access if (this.isCodeCommitGit) this.datetime = this.datetime.slice(0, -1) } return this.datetime}
RequestSigner.prototype.getDate = function() { return this.getDateTime().substr(0, 8)}
RequestSigner.prototype.authHeader = function() { return [ 'AWS4-HMAC-SHA256 Credential=' + this.credentials.accessKeyId + '/' + this.credentialString(), 'SignedHeaders=' + this.signedHeaders(), 'Signature=' + this.signature(), ].join(', ')}
RequestSigner.prototype.signature = function() { var date = this.getDate(), cacheKey = [this.credentials.secretAccessKey, date, this.region, this.service].join(), kDate, kRegion, kService, kCredentials = credentialsCache.get(cacheKey) if (!kCredentials) { kDate = hmac('AWS4' + this.credentials.secretAccessKey, date) kRegion = hmac(kDate, this.region) kService = hmac(kRegion, this.service) kCredentials = hmac(kService, 'aws4_request') credentialsCache.set(cacheKey, kCredentials) } return hmac(kCredentials, this.stringToSign(), 'hex')}
RequestSigner.prototype.stringToSign = function() { return [ 'AWS4-HMAC-SHA256', this.getDateTime(), this.credentialString(), hash(this.canonicalString(), 'hex'), ].join('\n')}
RequestSigner.prototype.canonicalString = function() { if (!this.parsedPath) this.prepareRequest()
var pathStr = this.parsedPath.path, query = this.parsedPath.query, headers = this.request.headers, queryStr = '', normalizePath = this.service !== 's3', decodePath = this.service === 's3' || this.request.doNotEncodePath, decodeSlashesInPath = this.service === 's3', firstValOnly = this.service === 's3', bodyHash
if (this.service === 's3' && this.request.signQuery) { bodyHash = 'UNSIGNED-PAYLOAD' } else if (this.isCodeCommitGit) { bodyHash = '' } else { bodyHash = headers['X-Amz-Content-Sha256'] || headers['x-amz-content-sha256'] || hash(this.request.body || '', 'hex') }
if (query) { var reducedQuery = Object.keys(query).reduce(function(obj, key) { if (!key) return obj obj[encodeRfc3986Full(key)] = !Array.isArray(query[key]) ? query[key] : (firstValOnly ? query[key][0] : query[key]) return obj }, {}) var encodedQueryPieces = [] Object.keys(reducedQuery).sort().forEach(function(key) { if (!Array.isArray(reducedQuery[key])) { encodedQueryPieces.push(key + '=' + encodeRfc3986Full(reducedQuery[key])) } else { reducedQuery[key].map(encodeRfc3986Full).sort() .forEach(function(val) { encodedQueryPieces.push(key + '=' + val) }) } }) queryStr = encodedQueryPieces.join('&') } if (pathStr !== '/') { if (normalizePath) pathStr = pathStr.replace(/\/{2,}/g, '/') pathStr = pathStr.split('/').reduce(function(path, piece) { if (normalizePath && piece === '..') { path.pop() } else if (!normalizePath || piece !== '.') { if (decodePath) piece = decodeURIComponent(piece).replace(/\+/g, ' ') path.push(encodeRfc3986Full(piece)) } return path }, []).join('/') if (pathStr[0] !== '/') pathStr = '/' + pathStr if (decodeSlashesInPath) pathStr = pathStr.replace(/%2F/g, '/') }
return [ this.request.method || 'GET', pathStr, queryStr, this.canonicalHeaders() + '\n', this.signedHeaders(), bodyHash, ].join('\n')}
RequestSigner.prototype.canonicalHeaders = function() { var headers = this.request.headers function trimAll(header) { return header.toString().trim().replace(/\s+/g, ' ') } return Object.keys(headers) .sort(function(a, b) { return a.toLowerCase() < b.toLowerCase() ? -1 : 1 }) .map(function(key) { return key.toLowerCase() + ':' + trimAll(headers[key]) }) .join('\n')}
RequestSigner.prototype.signedHeaders = function() { return Object.keys(this.request.headers) .map(function(key) { return key.toLowerCase() }) .sort() .join(';')}
RequestSigner.prototype.credentialString = function() { return [ this.getDate(), this.region, this.service, 'aws4_request', ].join('/')}
RequestSigner.prototype.defaultCredentials = function() { var env = process.env return { accessKeyId: env.AWS_ACCESS_KEY_ID || env.AWS_ACCESS_KEY, secretAccessKey: env.AWS_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY, sessionToken: env.AWS_SESSION_TOKEN, }}
RequestSigner.prototype.parsePath = function() { var path = this.request.path || '/'
// S3 doesn't always encode characters > 127 correctly and // all services don't encode characters > 255 correctly // So if there are non-reserved chars (and it's not already all % encoded), just encode them all if (/[^0-9A-Za-z;,/?:@&=+$\-_.!~*'()#%]/.test(path)) { path = encodeURI(decodeURI(path)) }
var queryIx = path.indexOf('?'), query = null
if (queryIx >= 0) { query = querystring.parse(path.slice(queryIx + 1)) path = path.slice(0, queryIx) }
this.parsedPath = { path: path, query: query, }}
RequestSigner.prototype.formatPath = function() { var path = this.parsedPath.path, query = this.parsedPath.query
if (!query) return path
// Services don't support empty query string keys if (query[''] != null) delete query['']
return path + '?' + encodeRfc3986(querystring.stringify(query))}
aws4.RequestSigner = RequestSigner
aws4.sign = function(request, credentials) { return new RequestSigner(request, credentials).sign()}