New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More

dynamicscrm-api

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dynamicscrm-api - npm Package Compare versions

Comparing version 0.0.12 to 0.1.0

/*jslint nomen: true, stupid: true */
// module dependencies
var https = require('https');
var url = require('url');
var xpath = require('xpath');
var cookie = require('cookie');
var Cache = require("mem-cache");
var uuid = require("node-uuid");
var domParser = new (require('xmldom').DOMParser)();
var path = require("path");
var urljs = require("url");
var fs = require("fs");
var parseString = require('xml2js').parseString;
var traverse = require('traverse');
var Serializer = require('./serializer.js');
var util = require('util');
var WSTrustFlow = require('../lib/ws-security/wsTrustFlow.js');
var constants = require("constants");
var xpath = require('xpath');
var Cache = require("mem-cache");
var uuid = require("node-uuid");
var domParser = new (require('xmldom').DOMParser)();
var fs = require("fs");
var parseString = require('xml2js').parseString;
var traverse = require('traverse');
var Serializer = require('./serializer.js');
var WSTrustFlow = require('../lib/ws-security/wsTrustFlow.js');
var constants = require("constants");
var cookie = require('cookie');
var httpntlm = require('httpntlm');
var ntlm = require('httpntlm/ntlm.js');
var Agentkeepalive = require('agentkeepalive');
var request = require('request');
// this class implements all features

@@ -25,3 +25,3 @@ var Util = function (settings) {

var authenticationTypes = ["live_id", "microsoft_online", "federation"];
var authenticationTypes = ["live_id", "microsoft_online", "federation", "ntlm"];

@@ -35,2 +35,4 @@ // Arguments validation

if (settings.password && typeof settings.password !== "string") { throw new Error("'settings.password' property must be a string."); }
if (settings.port && typeof settings.port !== "number") { throw new Error("'settings.port' property must be a number."); }
if (settings.organizationName && typeof settings.organizationName !== "string") { throw new Error("'settings.organizationName' property must be a string."); }

@@ -42,6 +44,6 @@ //Set default value if authentication type is wrong or invalid

settings.timeout = settings.timeout || 15 * 60 * 1000; // default sessions timeout of 15 minutes in ms
settings.returnJson = true; // default sessions timeout of 15 minutes in ms
settings.returnJson = true;
settings.port = settings.port || (settings.useHttp ? 80 : 443);
var self = this,
defaultUrlSuffix = ".api.crm.dynamics.com",
var defaultUrlSuffix = ".api.crm.dynamics.com",

@@ -58,2 +60,4 @@ getHostname = function () {

organizationServiceEndpoint = 'https://' + hostname + organizationPath,
userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",
SOAPActionBase = "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/",
cacheTokenByAuth = new Cache(settings.timeout),

@@ -69,3 +73,2 @@ cacheAuthByUser = new Cache(settings.timeout),

generateRandom,
getErrorMessage,
renameKey,

@@ -78,2 +81,5 @@ executeSoapPost,

authenticateUsingFederation,
authenticateUsingNTLM,
addSecureOptions,
parseResponse,

@@ -111,2 +117,10 @@ //load templates once

addSecureOptions = function (reqOptions) {
if (!settings.useHttp) {
reqOptions.secureOptions = constants.SSL_OP_NO_TLSv1_2;
reqOptions.ciphers = 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM';
reqOptions.honorCipherOrder = true;
}
};
fetchEndpoints = function (cb) {

@@ -116,90 +130,67 @@ if (endpoints) {

}
var options = {
host: hostname,
path: organizationPath + "?wsdl",
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
},
uri: settings.useHttp ? "http://" : "https://" + hostname + ":" + settings.port + organizationPath + "?wsdl",
};
response = https.get(options, function (res) {
var xml = '';
res.setEncoding('utf8');
addSecureOptions(options);
res.on('data', function (chunk) {
xml += chunk;
});
request(options, function (err, res, body) {
if (err) { return cb(err); }
res.on('end', function () {
var resXml = domParser.parseFromString(xml),
fault = xpath.select(faultTextXpath, resXml),
location,
opts;
var resXml = domParser.parseFromString(body),
fault = xpath.select(faultTextXpath, resXml),
location,
opts;
if (fault.length > 0) { return cb(new Error(fault.toString()), null); }
if (fault.length > 0) { return cb(new Error(fault.toString()), null); }
location = xpath.select(importLocationXpath, resXml)
.map(function (attr) {
return attr.value;
})[0];
location = xpath.select(importLocationXpath, resXml)
.map(function (attr) {
return attr.value;
})[0];
if (location.length > 0) {
opts = {
host: urljs.parse(location).host,
path: urljs.parse(location).pathname + urljs.parse(location).search,
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
};
if (location.length > 0) {
opts = { url: location };
https.get(opts, function (res) {
var xmlImport = '',
resXmlImport,
faultImport,
authenticationType,
issuerAddress,
liveAppliesTo,
identifier,
keyType,
keySize,
requireClientEntropy;
addSecureOptions(opts);
res.setEncoding('utf8');
request(opts, function (err, res, body) {
if (err) { return cb(err); }
res.on('data', function (chunk) {
xmlImport += chunk;
});
var resXmlImport,
faultImport,
authenticationType,
issuerAddress,
liveAppliesTo,
identifier,
keyType,
keySize,
requireClientEntropy;
res.on('end', function () {
resXmlImport = domParser.parseFromString(xmlImport);
faultImport = xpath.select(faultTextXpath, resXmlImport);
if (faultImport.length > 0) { return cb(new Error(faultImport.toString()), null); }
resXmlImport = domParser.parseFromString(body);
faultImport = xpath.select(faultTextXpath, resXmlImport);
if (faultImport.length > 0) { return cb(new Error(faultImport.toString()), null); }
authenticationType = xpath.select(authenticationTypeXpath, resXmlImport).toString();
issuerAddress = xpath.select(issuerAddressXpath, resXmlImport).toString();
liveAppliesTo = xpath.select(liveAppliesToXpath, resXmlImport).toString();
identifier = xpath.select("//*[local-name()='Identifier']/text()", resXmlImport).toString();
keyType = xpath.select("//*[local-name()='KeyType']/text()", resXmlImport).toString();
keySize = xpath.select("//*[local-name()='KeySize']/text()", resXmlImport).toString();
requireClientEntropy = (xmlImport.indexOf("RequireClientEntropy") > -1);
authenticationType = xpath.select(authenticationTypeXpath, resXmlImport).toString();
issuerAddress = xpath.select(issuerAddressXpath, resXmlImport).toString();
liveAppliesTo = xpath.select(liveAppliesToXpath, resXmlImport).toString();
identifier = xpath.select("//*[local-name()='Identifier']/text()", resXmlImport).toString();
keyType = xpath.select("//*[local-name()='KeyType']/text()", resXmlImport).toString();
keySize = xpath.select("//*[local-name()='KeySize']/text()", resXmlImport).toString();
requireClientEntropy = (body.indexOf("RequireClientEntropy") > -1);
endpoints = {
AuthenticationType: authenticationType,
IssuerAddress: issuerAddress,
DeviceAddUrl: "https://login.live.com/ppsecure/DeviceAddCredential.srf",
LiveIdAppliesTo: liveAppliesTo,
Identifier: identifier,
KeyType: keyType,
KeySize: keySize,
RequireClientEntropy: requireClientEntropy
};
return cb(null, endpoints);
});
});
}
endpoints = {
AuthenticationType: authenticationType,
IssuerAddress: issuerAddress,
DeviceAddUrl: "https://login.live.com/ppsecure/DeviceAddCredential.srf",
LiveIdAppliesTo: liveAppliesTo,
Identifier: identifier,
KeyType: keyType,
KeySize: keySize,
RequireClientEntropy: requireClientEntropy
};
return cb(null, endpoints);
});
});
response.on('error', function (err) {
return cb(new Error(getErrorMessage(err)));
}
});

@@ -214,4 +205,3 @@ };

var username = generateRandom(24, 'aA#'),
password = generateRandom(24, 'aA#'),
req;
password = generateRandom(24, 'aA#');

@@ -225,45 +215,31 @@ authCreateDeviceMessage = authCreateDeviceMessage

method: 'POST',
host: urljs.parse(options.DeviceAddUrl).host,
path: urljs.parse(options.DeviceAddUrl).pathname,
uri: options.DeviceAddUrl,
body: authCreateDeviceMessage,
headers: {
'Content-Type': 'application/soap+xml; charset=UTF-8',
'Content-Length': authCreateDeviceMessage.length
},
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
}
};
req = https.request(options, function (res) {
var xml = '';
res.setEncoding('utf8');
addSecureOptions(options);
res.on('data', function (chunk) {
xml += chunk;
});
request(options, function (err, res, body) {
if (err) { return cb(err); }
res.on('end', function () {
var resXml = domParser.parseFromString(xml),
fault = xpath.select(faultTextXpath, resXml),
puid;
var resXml = domParser.parseFromString(body),
fault = xpath.select(faultTextXpath, resXml),
puid;
if (fault.length > 0) { return cb(new Error(fault.toString()), null); }
if (fault.length > 0) { return cb(new Error(fault.toString()), null); }
puid = xpath.select("/DeviceAddResponse/puid/text()", resXml).toString();
puid = xpath.select("/DeviceAddResponse/puid/text()", resXml).toString();
device = {
deviceUsername : username,
devicePassword : password,
puid : puid
};
device = {
deviceUsername : username,
devicePassword : password,
puid : puid
};
return cb(null, device);
});
return cb(null, device);
});
req.on('error', function (err) {
return cb(new Error(getErrorMessage(err)));
});
req.end(authCreateDeviceMessage);
};

@@ -275,4 +251,3 @@

cipher = tokensForDeviceCache.get("auth_tokenrequest_device"),
requestOptions,
req;
requestOptions;

@@ -294,42 +269,28 @@ if (cipher) {

method: 'POST',
host: urljs.parse(options.IssuerAddress).host,
path: urljs.parse(options.IssuerAddress).pathname,
uri: options.IssuerAddress,
body: authRequestDeviceTokenMessage,
headers: {
'Content-Type': 'application/soap+xml; charset=UTF-8',
'Content-Length': authRequestDeviceTokenMessage.length
},
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
'Content-Length': Buffer.byteLength(authRequestDeviceTokenMessage)
}
};
req = https.request(requestOptions, function (res) {
var xml = '';
res.setEncoding('utf8');
addSecureOptions(requestOptions);
res.on('data', function (chunk) {
xml += chunk;
});
request(requestOptions, function (err, res, body) {
if (err) { return cb(err); }
res.on('end', function () {
var resXml = domParser.parseFromString(xml),
fault = xpath.select(faultTextXpath, resXml),
cipherValue;
var resXml = domParser.parseFromString(body),
fault = xpath.select(faultTextXpath, resXml),
cipherValue;
if (fault.length > 0) { return cb(new Error(fault.toString()), null); }
if (fault.length > 0) { return cb(new Error(fault.toString()), null); }
cipherValue = xpath.select("//*[local-name()='RequestedSecurityToken' and namespace-uri()='http://schemas.xmlsoap.org/ws/2005/02/trust']/*[name()='EncryptedData']/*[name()='CipherData']/*[name()='CipherValue']/text()", resXml).toString();
cipher = {CipherValue : cipherValue};
cipherValue = xpath.select("//*[local-name()='RequestedSecurityToken' and namespace-uri()='http://schemas.xmlsoap.org/ws/2005/02/trust']/*[name()='EncryptedData']/*[name()='CipherData']/*[name()='CipherValue']/text()", resXml).toString();
cipher = {CipherValue : cipherValue};
tokensForDeviceCache.set("auth_tokenrequest_device", cipher);
tokensForDeviceCache.set("auth_tokenrequest_device", cipher);
return cb(null, cipher);
});
return cb(null, cipher);
});
req.on('error', function (err) {
return cb(new Error(getErrorMessage(err)));
});
req.end(authRequestDeviceTokenMessage);
};

@@ -354,6 +315,2 @@

getErrorMessage = function (err) {
return (err.code === 'ENOTFOUND' ? 'Invalid host name' : err.toString()) + '\n' + err.stack;
};
renameKey = function (objInd, prefixes) {

@@ -369,2 +326,39 @@ var rk = objInd;

parseResponse = function (body, cb) {
var data = body,
prefixes,
data_no_ns,
resXml = domParser.parseFromString(body),
fault = xpath.select(faultTextXpath, resXml);
if (fault.length > 0) { return cb(new Error(fault.toString())); }
if (settings.returnJson) {
parseString(body, {explicitArray: false}, function (err, jsondata) {
if (err) { return cb(err); }
prefixes = [];
//removes namespaces
data_no_ns = traverse(jsondata).map(function () {
if (this.key !== undefined) {
var pos = this.key.indexOf('xmlns:'),
k = this.key.substring(6, this.key.length) + ':';
if (pos > -1 || this.key.indexOf('xmlns') > -1) {
if (prefixes.lastIndexOf(k) === -1) {
prefixes.push(k);
}
this.remove();
}
}
});
//removes 'xx:' prefixes
data = deepObjCopy(data_no_ns, prefixes);
cb(null, data);
});
} else {
cb(null, data);
}
};
executeSoapPost = function (options, action, template, body, cb) {

@@ -374,98 +368,125 @@ var timeCreated = new Date(),

requestOptions,
req,
soapHeader,
xmlrequestbody,
soapPostMessage,
security;
security,
ntlmOptions,
type1msg,
agent,
reqOptions,
url,
httpHeaders = {};
soapHeader = soapHeaderMessage
.replace("{action}", action)
.replace("{messageid}", uuid.v4())
.replace("{crmurl}", organizationServiceEndpoint);
xmlrequestbody = template.replace("{requetbody}", body);
if (options.encryptedData) {
security = '<wsse:Security s:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">\
<u:Timestamp u:Id="_0">\
<u:Created>' + timeCreated.toISOString() + '</u:Created>\
<u:Expires>' + timeExpires.toISOString() + '</u:Expires>\
</u:Timestamp>' + options.encryptedData + '</wsse:Security>';
if (settings.authType === "ntlm") {
soapPostMessage = soapEnvelopeMessage
.replace("{envelopeNS}", "http://schemas.xmlsoap.org/soap/envelope/")
.replace("{header}", "")
.replace("{body}", xmlrequestbody);
soapHeader = soapHeader.replace("{security}", security);
} else if (options.header) {
soapHeader = soapHeader.replace("{security}", options.header);
} else {
return cb(new Error("Neither token or header found."));
}
url = (settings.useHttp ? "http://" : "https://") + hostname + ":" + settings.port + "/" + settings.organizationName + organizationPath + "/web";
xmlrequestbody = template.replace("{requetbody}", body);
httpHeaders.cookie = "ReqClientId=" + options.ReqClientId;
httpHeaders.SOAPAction = SOAPActionBase + action;
httpHeaders['Content-Length'] = Buffer.byteLength(soapPostMessage);
httpHeaders['Content-Type'] = "text/xml; charset=utf-8";
httpHeaders.Accept = 'application/xml, text/xml, */*';
httpHeaders["User-Agent"] = userAgent;
soapPostMessage = soapEnvelopeMessage
.replace("{header}", soapHeader)
.replace("{body}", xmlrequestbody);
ntlmOptions = {
username : options.username || settings.username,
password : options.password || settings.password,
workstation : options.workstation || settings.workstation || '',
domain : options.ntlmDomain || settings.ntlmDomain || ''
};
requestOptions = {
method: 'POST',
host: hostname,
path: '/XRMServices/2011/Organization.svc',
headers: {
'Content-Type': 'application/soap+xml; charset=UTF-8',
'Content-Length': soapPostMessage.length
},
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
};
type1msg = ntlm.createType1Message(ntlmOptions);
agent = settings.useHttp ? new Agentkeepalive() : new Agentkeepalive.HttpsAgent();
req = https.request(requestOptions, function (res) {
var xml = '';
res.setEncoding('utf8');
reqOptions = {
method: options.method || "GET",
url: url,
headers: {
Authorization: type1msg,
},
agent: agent,
timeout : settings.requestTimeout
};
res.on('data', function (chunk) {
xml += chunk;
addSecureOptions(reqOptions);
request(reqOptions, function (err, res) {
if (err) { return cb(err); }
if (!res.headers['www-authenticate']) {
return cb(new Error('www-authenticate not found on response of second request'));
}
var type2msg = ntlm.parseType2Message(res.headers['www-authenticate']),
type3msg = ntlm.createType3Message(type2msg, ntlmOptions);
httpHeaders.Authorization = type3msg;
reqOptions = {
method : "POST",
url: url,
body: soapPostMessage,
agent: agent,
timeout : settings.requestTimeout,
headers: httpHeaders
};
addSecureOptions(reqOptions);
request(reqOptions, function (err, res, body) {
if (err) { return cb(err); }
parseResponse(body, cb);
});
});
res.on('end', function () {
var resXml = domParser.parseFromString(xml),
fault = xpath.select(faultTextXpath, resXml),
data,
//jsondata,
prefixes,
data_no_ns;
} else {
soapHeader = soapHeaderMessage
.replace("{action}", action)
.replace("{messageid}", uuid.v4())
.replace("{crmurl}", organizationServiceEndpoint);
if (fault.length > 0) { return cb(new Error(fault.toString())); }
if (options.encryptedData) {
security = '<wsse:Security s:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">\
<u:Timestamp u:Id="_0">\
<u:Created>' + timeCreated.toISOString() + '</u:Created>\
<u:Expires>' + timeExpires.toISOString() + '</u:Expires>\
</u:Timestamp>' + options.encryptedData + '</wsse:Security>';
data = xml;
if (settings.returnJson) {
parseString(xml, {explicitArray: false}, function (err, jsondata) {
if (err) { return cb(err); }
soapHeader = soapHeader.replace("{security}", security);
} else if (options.header) {
soapHeader = soapHeader.replace("{security}", options.header);
} else {
return cb(new Error("Neither token or header found."));
}
prefixes = [];
//removes namespaces
data_no_ns = traverse(jsondata).map(function () {
if (this.key !== undefined) {
var pos = this.key.indexOf('xmlns:'),
k = this.key.substring(6, this.key.length) + ':';
url = (settings.useHttp ? "http://" : "https://") + hostname + ":" + settings.port + organizationPath;
soapPostMessage = soapEnvelopeMessage
.replace("{envelopeNS}", "http://www.w3.org/2003/05/soap-envelope")
.replace("{header}", soapHeader)
.replace("{body}", xmlrequestbody);
if (pos > -1 || this.key.indexOf('xmlns') > -1) {
if (prefixes.lastIndexOf(k) === -1) {
prefixes.push(k);
}
this.remove();
}
}
});
//removes 'xx:' prefixes
data = deepObjCopy(data_no_ns, prefixes);
cb(null, data);
});
} else {
cb(null, data);
}
httpHeaders['Content-Type'] = 'application/soap+xml; charset=UTF-8';
httpHeaders['Content-Length'] = Buffer.byteLength(soapPostMessage);
requestOptions = {
method: 'POST',
uri: url,
body: soapPostMessage,
headers: httpHeaders
};
addSecureOptions(requestOptions);
request(requestOptions, function (err, res, body) {
if (err) { return cb(err); }
parseResponse(body, cb);
});
});
req.on('error', function (err) {
return cb(new Error(getErrorMessage(err)));
});
req.end(soapPostMessage);
}
};

@@ -524,34 +545,24 @@

method: 'POST',
host: host,
path: path,
headers: { 'Content-Length': samlRequest.length },
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
},
uri: "https://" + host + path,
body: samlRequest,
headers: { 'Content-Length': Buffer.byteLength(samlRequest) }
};
req = https.request(options, function (res) {
var xml = '';
res.setEncoding('utf8');
addSecureOptions(options);
res.on('data', function (chunk) {
xml += chunk;
});
request(options, function (err, res, body) {
if (err) { return cb(err); }
res.on('end', function () {
// parses XML
var resXml = domParser.parseFromString(xml),
var resXml = domParser.parseFromString(body),
// search for a fault
exp = ['S:Envelope', 'S:Body', 'S:Fault', 'S:Detail', 'psf:error', 'psf:internalerror', 'psf:text'].map(name).join("") + "/text()",
fault = xpath.select(exp, resXml);
// search for a fault
exp = ['S:Envelope', 'S:Body', 'S:Fault', 'S:Detail', 'psf:error', 'psf:internalerror', 'psf:text'].map(name).join("") + "/text()",
fault = xpath.select(exp, resXml);
if (fault.length > 0) {
return cb(new Error(fault.toString()));
}
if (fault.length > 0) {
return cb(new Error(fault.toString()));
}
return cb(null, resXml);
});
});
req.end(samlRequest);
return cb(null, resXml);
});
};

@@ -577,4 +588,3 @@

timeExpires = new Date(timeCreated.getTime() + settings.timeout),
requestOptions,
req;
requestOptions;

@@ -593,54 +603,39 @@ authOptions.cipherValue = result.CipherValue;

method: 'POST',
host: urljs.parse(authOptions.IssuerAddress).host,
path: urljs.parse(authOptions.IssuerAddress).pathname,
uri: authOptions.IssuerAddress,
body: authRequestSTSTokenMessage,
headers: {
'Content-Type': 'application/soap+xml; charset=UTF-8',
'Content-Length': authRequestSTSTokenMessage.length
},
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
'Content-Length': Buffer.byteLength(authRequestSTSTokenMessage)
}
};
req = https.request(requestOptions, function (res) {
var xml = '';
addSecureOptions(requestOptions);
res.setEncoding('utf8');
request(requestOptions, function (err, res, body) {
if (err) { return cb(err); }
res.on('data', function (chunk) {
xml += chunk;
});
var resXml = domParser.parseFromString(body),
fault = xpath.select(faultTextXpath, resXml),
fullMessage,
faultDetailsXpath,
faultDetails;
res.on('end', function () {
var resXml = domParser.parseFromString(xml),
fault = xpath.select(faultTextXpath, resXml),
fullMessage,
faultDetailsXpath,
faultDetails;
if (fault.length > 0) {
fullMessage = fault.toString();
faultDetailsXpath = "//*[local-name()='Fault']/*[local-name()='Detail']";
faultDetails = xpath.select(faultDetailsXpath, resXml);
if (fault.length > 0) {
fullMessage = fault.toString();
faultDetailsXpath = "//*[local-name()='Fault']/*[local-name()='Detail']";
faultDetails = xpath.select(faultDetailsXpath, resXml);
if (faultDetails.length > 0) {
parseString(faultDetails.toString(), {explicitArray: false}, function (err, data) {
if (err) { return cb(err); }
if (faultDetails.length > 0) {
parseString(faultDetails.toString(), {explicitArray: false}, function (err, data) {
if (err) { return cb(err); }
fullMessage = fullMessage + ". Details:" + data;
return cb(new Error(fullMessage), null);
});
}
} else {
return cb(null, resXml);
fullMessage = fullMessage + ". Details:" + data;
return cb(new Error(fullMessage), null);
});
}
} else {
return cb(null, resXml);
}
});
});
req.on('error', function (err) {
return cb(new Error(getErrorMessage(err)));
});
req.end(authRequestSTSTokenMessage);
});

@@ -682,6 +677,35 @@ });

authenticateUsingNTLM = function (options, cb) {
var authOptions = {
url: (settings.useHttp ? "http://" : "https://") + hostname + ":" + settings.port,
username : options.username || settings.username,
password : options.password || settings.password,
workstation : options.workstation || settings.workstation || '',
domain : options.ntlmDomain || settings.ntlmDomain || '',
headers: {
"User-Agent": userAgent
}
};
httpntlm.get(authOptions, function (err, res) {
if (err) { return cb(err); }
if (res.cookies.length === 0) { return cb(Error.create("Invalid Username or Password")); }
var cookies = cookie.parse(res.headers["set-cookie"].join(";")),
authToken = uuid.v4(),
session = {
username: options.username,
password: options.password,
ReqClientId: cookies.ReqClientId
};
cacheTokenByAuth.set(authToken, session);
cacheAuthByUser.set(options.username, authToken);
return cb(null, {auth: authToken, username: options.username});
});
};
this.Authenticate = function (options, cb) {
var item,
responseXMLCB = function (err, resXml) {
var responseXMLCB = function (err, resXml) {
if (err) { return cb(err); }

@@ -730,2 +754,4 @@

authenticateUsingFederation(options, federationCB);
} else if (settings.authType === "ntlm") {
authenticateUsingNTLM(options, cb);
} else { //Default Live Id

@@ -792,2 +818,3 @@ authenticateUsingLiveId(options, responseXMLCB);

this.executePost = function (options, action, template, body, cb) {
var authItem;
// handles optional 'options' argument

@@ -809,5 +836,6 @@ if (!cb && typeof options === 'function') {

} else if (options.auth) {
var authItem = cacheTokenByAuth.get(options.auth);
authItem = cacheTokenByAuth.get(options.auth);
options.encryptedData = authItem.token; //For LiveId an MSOnline
options.header = authItem.header; //For Federation
options.ReqClientId = authItem.ReqClientId; //For NTLM

@@ -819,3 +847,3 @@ executeSoapPost(options, action, template, body, cb);

var authItem = cacheTokenByAuth.get(data.auth);
authItem = cacheTokenByAuth.get(data.auth);
options.encryptedData = authItem.token; //For LiveId an MSOnline

@@ -822,0 +850,0 @@ options.header = authItem.header; //For Federation

{
"name": "dynamicscrm-api",
"version": "0.0.12",
"version": "0.1.0",
"description": "Pure SOAP module that allows to invoke Microsoft Dynamics CRM Online services",

@@ -38,6 +38,8 @@ "author": "Kidozen <development@kidozen.com>",

"traverse": "0.6.5",
"url": "0.7.9",
"xml2js": "0.4.4",
"xmldom": "0.1.19",
"xpath": "0.0.6"
"xpath": "0.0.6",
"agentkeepalive": "0.1.5",
"httpntlm": "https://github.com/kidozen/node-http-ntlm/tarball/master",
"request": "2.44.0"
},

@@ -44,0 +46,0 @@ "devDependencies": {

@@ -29,2 +29,13 @@ /*global describe, before, beforeEach, it */

authType: "federation"
},
settingsForNTLMAuth = {
username: "",
password: "",
organizationName: "",
domain: "",
domainUrlSuffix: "",
port: 5555,
useHttp: true,
authType: "ntlm"
};

@@ -142,3 +153,3 @@

describe("Office 365 Authentication", function () {
describe("Microsoft Online (Office 365) Authentication", function () {
var settings,

@@ -160,7 +171,6 @@ dynamics;

it("Should authenticate OK", function (done) {
it("Should fail with password expired", function (done) {
dynamics.Authenticate({}, function (err, result) {
assert.ok(!err);
assert.ok(result);
assert.ok(result.auth);
assert.ok(err);
assert.strictEqual(err.message, "The password for the account has expired.\r\n");
done();

@@ -198,2 +208,32 @@ });

describe("NTLM Authentication", function () {
var settings,
dynamics;
beforeEach(function () {
settings = settingsForNTLMAuth;
dynamics = new Dynamics(settings);
});
it.skip("Should fail with invalid credentials", function (done) {
dynamics.Authenticate({username: "invalid"}, function (err, result) {
assert.ifError(err);
assert.ok(!result);
done();
});
});
it("Should authenticate OK", function (done) {
dynamics.Authenticate({}, function (err, result) {
assert.ifError(err);
assert.ok(result);
assert.ok(result.auth);
assert.ok(result.username);
assert.equal('string', typeof result.auth);
assert.equal(36, result.auth.length);
done();
});
});
});
var shouldFailDeletionWithInvalidId = function (dynamics, done) {

@@ -348,2 +388,35 @@ var options = {};

});
describe("Method execution with NTLM Auth", function () {
var settings,
dynamics;
before(function () {
settings = settingsForNTLMAuth;
dynamics = new Dynamics(settings);
});
it("Should retrieve one result", function (done) {
dynamics.Authenticate({}, function (err, authData) {
assert.ok(!err, err);
assert.ok(authData);
assert.ok(authData.auth);
var id = "d4ea3aab-8263-e411-9446-22000b4712e7",
logicalName = "contact";
dynamics.Retrieve({auth: authData.auth,
"EntityName": logicalName,
"ColumnSet": ["fullname"],
"id": id}, function (err2, result2) {
assert.ok(!err2, err2);
assert.ok(result2);
assert.strictEqual(result2.Envelope.Body.RetrieveResponse.RetrieveResult.Id, id);
assert.strictEqual(result2.Envelope.Body.RetrieveResponse.RetrieveResult.LogicalName, logicalName);
done();
});
});
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet