Skip to content

Commit

Permalink
Added ntlm (challenge+response) security
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Tallman committed Nov 3, 2013
1 parent ac62402 commit 5dfc3df
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 35 deletions.
16 changes: 11 additions & 5 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,13 @@ Client.prototype._invoke = function(method, arguments, location, callback, optio
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.security.addOptions(options);


//NTLM Security has extra requirements
var ntlm = false;
if (self.security && self.security.NTLM){
ntlm = self.security;
}

if (input.parts) {
assert.ok(!style || style == 'rpc', 'invalid message definition for document style binding');
message = self.wsdl.objectToRpcXML(name, arguments, alias, ns);
Expand All @@ -121,7 +127,7 @@ Client.prototype._invoke = function(method, arguments, location, callback, optio
}
else {
assert.ok(!style || style == 'document', 'invalid message definition for rpc style binding');
message = self.wsdl.objectToDocumentXML(input.$name, arguments, input.targetNSAlias, input.targetNamespace);
message = self.wsdl.objectToDocumentXML(input.$name, arguments, input.targetNSAlias, input.targetNamespace);
}
xml = "<soap:Envelope " +
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
Expand All @@ -136,8 +142,8 @@ Client.prototype._invoke = function(method, arguments, location, callback, optio
"</soap:Body>" +
"</soap:Envelope>";

self.lastRequest = xml;
self.lastRequest = xml;

http.request(location, xml, function(err, response, body) {
self.lastResponse = body;
if (err) {
Expand All @@ -160,7 +166,7 @@ Client.prototype._invoke = function(method, arguments, location, callback, optio
}
callback(null, result, body);
}
}, headers, options);
}, headers, options, ntlm);
}

exports.Client = Client;
38 changes: 27 additions & 11 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
*/

var url = require('url'),
req = require('request');
req = require('request'),
httpntlm = require('httpntlm');

var VERSION = "0.2.0";

exports.request = function(rurl, data, callback, exheaders, exoptions) {
exports.request = function(rurl, data, callback, exheaders, exoptions, ntlmSecurity) {
var curl = url.parse(rurl);
var secure = curl.protocol == 'https:';
var host = curl.hostname;
Expand Down Expand Up @@ -41,13 +42,28 @@ exports.request = function(rurl, data, callback, exheaders, exoptions) {
exoptions = exoptions || {};
for (var attr in exoptions) { options[attr] = exoptions[attr]; }

var request = req(options, function (error, res, body) {
if (error) {
callback(error);
} else {
callback(null, res, body);
}
});
request.on('error', callback);
request.end(data);
console.log(JSON.stringify(headers));

if(!ntlmSecurity){

var request = req(options, function (error, res, body) {
if (error) {
callback(error);
} else {
callback(null, res, body);
}
});
request.on('error', callback);
request.end(data);

}else{
console.log('Entering NTLM request');
options.headers['Authorization'] = ntlm.challengeHeader(ntlmSecurity._hostname, ntlmSecurity._domain);
options.workstation = 'virco.webserver';
options.body = data;
options.domain = ntlmSecurity._domain;
options.username = ntlmSecurity._username;
options.password = ntlmSecurity._password;
httpntlm.post(options, callback);
}
}
17 changes: 17 additions & 0 deletions lib/soap.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ function listen(server, pathOrOptions, services, xml) {
return new Server(server, path, services, wsdl);
}

function NTLMAuthSecurity(url, domain, username, password) {
this._url = url;
this._hostname = require('url').parse(url).hostname;
this._domain = domain;
this._username = username;
this._password = password;
}

NTLMAuthSecurity.prototype.toXML = function() {
return "";
}

NTLMAuthSecurity.prototype.NTLM = function() {
return true;
}

function BasicAuthSecurity(username, password) {
this._username = username;
this._password = password;
Expand Down Expand Up @@ -144,6 +160,7 @@ WSSecurity.prototype.toXML = function() {
}

exports.BasicAuthSecurity = BasicAuthSecurity;
exports.NTLMAuthSecurity = NTLMAuthSecurity;
exports.WSSecurity = WSSecurity;
exports.ClientSSLSecurity = ClientSSLSecurity;
exports.createClient = createClient;
Expand Down
65 changes: 46 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
{
"name": "soap",
"version": "0.3.0",
"description": "A minimal node SOAP client",
"engines": { "node": ">=0.8.0" },
"author": "Vinay Pulim <v@pulim.com>",
"dependencies": {
"node-expat": ">=1.6.1",
"request": ">=2.9.0"
},
"repository" : {
"type":"git",
"url":"https://github.com/milewise/node-soap.git" },
"main": "./index.js",
"directories": { "lib": "./lib" },
"scripts": {"test": "mocha -R spec -u exports test/*-test.js"},
"keywords": ["soap"],
"licenses": [{
"type" : "MIT License",
"url" : "http://www.opensource.org/licenses/mit-license.php" }]
"name": "soap",
"version": "0.3.0",
"description": "A minimal node SOAP client",
"engines": {
"node": ">=0.8.0"
},
"author": {
"name": "Vinay Pulim",
"email": "v@pulim.com"
},
"dependencies": {
"node-expat": ">=1.6.1",
"request": ">=2.9.0",
"httpntlm": "https://github.com/btallman/node-http-ntlm.git"
},
"repository": {
"type": "git",
"url": "https://github.com/milewise/node-soap.git"
},
"main": "./index.js",
"directories": {
"lib": "./lib"
},
"scripts": {
"test": "mocha -R spec -u exports test/*-test.js"
},
"keywords": [
"soap"
],
"licenses": [
{
"type": "MIT License",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
"readme": "This module lets you connect to web services using SOAP. It also provides a server that allows you to run your own SOAP services.\n\nFeatures:\n\n* Very simple API\n* Handles both RPC and Document schema types\n* Supports multiRef SOAP messages (thanks to [@kaven276](https://github.com/kaven276))\n* Support for both synchronous and asynchronous method handlers\n* WS-Security (currently only UsernameToken and PasswordText encoding is supported)\n* Slightly improved ability to handle SAP SOAP WSDLS\n 1. Sequences nest a collection of <item> objects beneath the parent object, instead of a collection of parent objects\n\n## Install\n\nInstall with [npm](http://github.com/isaacs/npm):\n\n```\n npm install soap\n```\n## Module\n\n### soap.createClient(url, callback) - create a new SOAP client from a WSDL url. Also supports a local filesystem path.\n\n``` javascript\n var soap = require('soap');\n var url = 'http://example.com/wsdl?wsdl';\n var args = {name: 'value'};\n soap.createClient(url, function(err, client) {\n client.MyFunction(args, function(err, result) {\n console.log(result);\n });\n });\n```\n\n### soap.listen(*server*, *path*, *services*, *wsdl*) - create a new SOAP server that listens on *path* and provides *services*.\n*wsdl* is an xml string that defines the service.\n\n``` javascript\n var myService = {\n MyService: {\n MyPort: {\n MyFunction: function(args) {\n return {\n name: args.name\n };\n }\n\n // This is how to define an asynchronous function. \n MyAsyncFunction: function(args, callback) {\n // do some work\n callback({\n name: args.name\n })\n }\n }\n }\n }\n\n var xml = require('fs').readFileSync('myservice.wsdl', 'utf8'),\n server = http.createServer(function(request,response) {\n response.end(\"404: Not Found: \"+request.url)\n });\n\n server.listen(8000);\n soap.listen(server, '/wsdl', myService, xml);\n```\n\n### server logging\n\nIf the log method is defined it will be called with 'received' and 'replied'\nalong with data.\n\n``` javascript\n server = soap.listen(...)\n server.log = function(type, data) {\n // type is 'received' or 'replied'\n };\n```\n\n### server security example using PasswordDigest\n\nIf server.authenticate is not defined no authentation will take place.\n\n``` javascript\n server = soap.listen(...)\n server.authenticate = function(security) {\n var created, nonce, password, user, token;\n token = security.UsernameToken, user = token.Username,\n password = token.Password, nonce = token.Nonce, created = token.Created;\n return user === 'user' && password === soap.passwordDigest(nonce, created, 'password');\n };\n```\n\n### server connection authorization\n\nThis is called prior to soap service method\nIf the method is defined and returns false the incoming connection is\nterminated.\n\n``` javascript\n server = soap.listen(...)\n server.authorizeConnection = function(req) {\n return true; // or false\n };\n```\n\n\n## Client\n\nAn instance of Client is passed to the soap.createClient callback. It is used to execute methods on the soap service.\n\n### Client.describe() - description of services, ports and methods as a JavaScript object\n\n``` javascript\n client.describe() // returns\n {\n MyService: {\n MyPort: {\n MyFunction: {\n input: {\n name: 'string'\n }\n }\n }\n }\n }\n```\n\n### Client.setSecurity(security) - use the specified security protocol (see WSSecurity below)\n\n``` javascript\n client.setSecurity(new WSSecurity('username', 'password'))\n```\n\n### Client.*method*(args, callback) - call *method* on the SOAP service.\n\n``` javascript\n client.MyFunction({name: 'value'}, function(err, result) {\n // result is a javascript object\n })\n```\n### Client.*service*.*port*.*method*(args, callback) - call a *method* using a specific *service* and *port*\n\n``` javascript\n client.MyService.MyPort.MyFunction({name: 'value'}, function(err, result) {\n // result is a javascript object\n })\n```\n### Client.*addSoapHeader*(soapHeader[, name, namespace, xmlns]) - add soapHeader to soap:Header node\n#### Options\n\n - `soapHeader` Object({rootName: {name: \"value\"}}) or strict xml-string\n\n##### Optional parameters when first arg is object :\n - `name` Unknown parameter (it could just a empty string)\n - `namespace` prefix of xml namespace\n - `xmlns` URI\n\n### Client.*lastRequest* - the property that contains last full soap request for client logging\n\n## WSSecurity\n\nWSSecurity implements WS-Security. UsernameToken and PasswordText/PasswordDigest is supported. An instance of WSSecurity is passed to Client.setSecurity.\n\n``` javascript\n new WSSecurity(username, password, passwordType)\n //'PasswordDigest' or 'PasswordText' default is PasswordText\n```\n",
"readmeFilename": "Readme.md",
"bugs": {
"url": "https://github.com/milewise/node-soap/issues"
},
"_id": "soap@0.3.0",
"dist": {
"shasum": "1cb44bad31fe51e218e54c78aaa4446c3a311e21"
},
"_resolved": "git://github.com/btallman/node-soap#ac6240218470ef142ffed64c6e1b70ed30835c26",
"_from": "soap@git://github.com/btallman/node-soap#alpha-1"
}

0 comments on commit 5dfc3df

Please sign in to comment.