Add SpoofedNodeHttpImpl
Session spoofing is needed for making authenticated requests.master
parent
d47d77bb5e
commit
65ab92f701
|
@ -98,9 +98,10 @@ module.exports = Class( 'DataApiManager' )
|
||||||
'private _apis': {},
|
'private _apis': {},
|
||||||
|
|
||||||
|
|
||||||
__construct: function( api_factory )
|
__construct: function( api_factory, apis )
|
||||||
{
|
{
|
||||||
this._dataApiFactory = api_factory;
|
this._dataApiFactory = api_factory;
|
||||||
|
this.setApis( apis || {} );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,5 +47,9 @@ module.exports = Interface( 'HttpImpl',
|
||||||
*
|
*
|
||||||
* @return {HttpImpl} self
|
* @return {HttpImpl} self
|
||||||
*/
|
*/
|
||||||
'public requestData': [ 'url', 'method', 'data', 'callback' ]
|
'public requestData': [ 'url', 'method', 'data', 'callback' ],
|
||||||
|
|
||||||
|
// TODO: temporary to work around class extension bug; see
|
||||||
|
// SpoofedNodeHttpImpl
|
||||||
|
'public setOptions': [],
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -98,7 +98,7 @@ module.exports = Class( 'NodeHttpImpl' )
|
||||||
throw Error( `No handler for ${protocol}` );
|
throw Error( `No handler for ${protocol}` );
|
||||||
}
|
}
|
||||||
|
|
||||||
this._setOptions( options, method, data );
|
this.setOptions( options, method, data );
|
||||||
|
|
||||||
let forbid_end = false;
|
let forbid_end = false;
|
||||||
|
|
||||||
|
@ -152,13 +152,16 @@ module.exports = Class( 'NodeHttpImpl' )
|
||||||
/**
|
/**
|
||||||
* Set request options
|
* Set request options
|
||||||
*
|
*
|
||||||
|
* TODO: public to work around a class extension trait bug; make
|
||||||
|
* protected once fixed
|
||||||
|
*
|
||||||
* @param {Object} options request options
|
* @param {Object} options request options
|
||||||
* @param {string} method HTTP method
|
* @param {string} method HTTP method
|
||||||
* @param {string} data request data
|
* @param {string} data request data
|
||||||
*
|
*
|
||||||
* @return {Object} request headers
|
* @return {Object} request headers
|
||||||
*/
|
*/
|
||||||
'private _setOptions'( options, method, data )
|
'virtual public setOptions'( options, method, data )
|
||||||
{
|
{
|
||||||
const { headers = {} } = options;
|
const { headers = {} } = options;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
* Node-based HTTP client with session spoofing
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of the Liza Data Collection Framework.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Trait } = require( 'easejs' );
|
||||||
|
const HttpImpl = require( './HttpImpl' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spoof user session during request
|
||||||
|
*
|
||||||
|
* TODO: Implementing HttpImpl instead of overriding NodeHttpImpl to work
|
||||||
|
* around a class extension bug; change once fixed.
|
||||||
|
*/
|
||||||
|
module.exports = Trait( 'SpoofedNodeHttpImpl' )
|
||||||
|
.implement( HttpImpl )
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Session to spoof
|
||||||
|
* @type {UserSession}
|
||||||
|
*/
|
||||||
|
'private _request': null,
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use session for spoofing requests
|
||||||
|
*
|
||||||
|
* @param {UserSession} session session to spoof
|
||||||
|
*/
|
||||||
|
__mixin( session )
|
||||||
|
{
|
||||||
|
this._request = session;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set request options to spoof session
|
||||||
|
*
|
||||||
|
* @param {Object} options request options
|
||||||
|
* @param {string} method HTTP method
|
||||||
|
* @param {string} data request data
|
||||||
|
*
|
||||||
|
* @return {Object} request headers
|
||||||
|
*/
|
||||||
|
'virtual abstract override public setOptions'( options, method, data )
|
||||||
|
{
|
||||||
|
const cookie = this._request.getSessionIdName() + '=' +
|
||||||
|
this._request.getSessionId();
|
||||||
|
|
||||||
|
options.headers = {
|
||||||
|
'User-Agent': this._request.getUserAgent(),
|
||||||
|
'X-Forwarded-For': this._request.getRemoteAddr(),
|
||||||
|
'Cookie': cookie,
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.__super( options, method, data );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -279,5 +279,11 @@ module.exports = Class( 'XhrHttpImpl' )
|
||||||
e.status = req.status;
|
e.status = req.status;
|
||||||
|
|
||||||
callback( e, req.responseText );
|
callback( e, req.responseText );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public setOptions'()
|
||||||
|
{
|
||||||
|
// TOOD: remove (see HttpImpl)
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -27,7 +27,11 @@ var dapi = require( '../../../' ).dapi,
|
||||||
dummy_url = 'http://foo',
|
dummy_url = 'http://foo',
|
||||||
dummy_impl = Class
|
dummy_impl = Class
|
||||||
.implement( dapi.http.HttpImpl )
|
.implement( dapi.http.HttpImpl )
|
||||||
.extend( { requestData: function( _, __, ___, ____ ) {} } )(),
|
.extend(
|
||||||
|
{
|
||||||
|
requestData: function( _, __, ___, ____ ) {},
|
||||||
|
setOptions() {},
|
||||||
|
} )(),
|
||||||
|
|
||||||
dummy_sut = Sut( dummy_url, 'GET', dummy_impl );
|
dummy_sut = Sut( dummy_url, 'GET', dummy_impl );
|
||||||
|
|
||||||
|
@ -86,7 +90,9 @@ describe( 'HttpDataApi', function()
|
||||||
{
|
{
|
||||||
this.provided = arguments;
|
this.provided = arguments;
|
||||||
c( this.err, this.data );
|
c( this.err, this.data );
|
||||||
}
|
},
|
||||||
|
|
||||||
|
setOptions() {},
|
||||||
} )();
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* Tests Node-based HTTP client with session spoofing
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of the Liza Data Collection Framework.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { expect } = require( 'chai' );
|
||||||
|
const {
|
||||||
|
SpoofedNodeHttpImpl: Sut,
|
||||||
|
NodeHttpImpl,
|
||||||
|
} = require( '../../../' ).dapi.http;
|
||||||
|
|
||||||
|
|
||||||
|
describe( 'SpoofNodeHttpImpl', () =>
|
||||||
|
{
|
||||||
|
it( "adds session headers", done =>
|
||||||
|
{
|
||||||
|
const user_agent = 'Agent Foo';
|
||||||
|
const forward_for = '::1';
|
||||||
|
const sessname = 'FOOSESSID';
|
||||||
|
const sessid = '12345';
|
||||||
|
|
||||||
|
const protos = {
|
||||||
|
http: {
|
||||||
|
request( given )
|
||||||
|
{
|
||||||
|
expect( given.headers[ 'User-Agent' ] )
|
||||||
|
.to.equal( user_agent );
|
||||||
|
expect( given.headers[ 'X-Forwarded-For' ] )
|
||||||
|
.to.equal( forward_for );
|
||||||
|
|
||||||
|
expect( given.headers.Cookie )
|
||||||
|
.to.contain( sessname + '=' + sessid );
|
||||||
|
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = {
|
||||||
|
parse: () => ( {
|
||||||
|
protocol: 'http',
|
||||||
|
} )
|
||||||
|
};
|
||||||
|
|
||||||
|
const session = getStubSession( {
|
||||||
|
agent: user_agent,
|
||||||
|
forward_for: forward_for,
|
||||||
|
sessname: sessname,
|
||||||
|
sessid: sessid,
|
||||||
|
} );
|
||||||
|
|
||||||
|
const given = NodeHttpImpl.use( Sut( session ) )( protos, url )
|
||||||
|
.requestData( '', '', {}, ()=>{} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
function getStubSession( { agent, forward_for, sessname, sessid } )
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
getUserAgent: () => agent,
|
||||||
|
getRemoteAddr: () => forward_for,
|
||||||
|
getSessionIdName: () => sessname,
|
||||||
|
getSessionId: () => sessid,
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue