UNPKG

wcf.js

Version:

A WCF-compatible web services client stack for node.js

289 lines (240 loc) 9.27 kB
var ws = require('ws.js') , utils = require('./utils.js') , FileKeyInfo = require('xml-crypto').FileKeyInfo exports.Proxy = Proxy exports.BasicHttpBinding = BasicHttpBinding exports.WSHttpBinding = WSHttpBinding exports.CustomBinding = CustomBinding exports.MtomMessageEncodingBindingElement = MtomMessageEncodingBindingElement exports.TextMessageEncodingBindingElement = TextMessageEncodingBindingElement exports.HttpTransportBindingElement = HttpTransportBindingElement exports.HttpsTransportBindingElement = HttpTransportBindingElement exports.SecurityBindingElement = SecurityBindingElement function Proxy(binding, url) { this.context = {url: url} this.binding = binding this.hanlders = null this.attachments = [] this.ClientCredentials = {} this.ClientCredentials.Username = {} this.ClientCredentials.ClientCertificate = {} this.ClientCredentials.ServiceCertificate = {} this.addAttachment = function (xpath, file) { this.attachments.push({xpath: xpath, file: file}) } this.getAttachment = function (xpath) { return ws.getAttachment(this.context, "response", xpath) } this.cleanAttachments = function () { this.attachments = [] } this.send = function(message, action, callback) { var custom = this.binding.getCustomBinding() if (this.handlers==null) { this.handlers = this.binding.getCustomBinding().getHandlers(this) } this.context.request = message this.context.action = action this.context.contentType = custom.getContentType() for (var i in this.attachments) { ws.addAttachment( this.context, "request", this.attachments[i].xpath, this.attachments[i].file, "application/octet-stream") } ws.send(this.handlers, this.context, function(ctx) { callback(ctx.response, ctx)}) } } function BasicHttpBinding(options) { utils.unifyProperties(options, this) this.getCustomBinding = function() { if (this.customBinding) return this.customBinding; var channels = [] if (this.MessageEncoding && this.MessageEncoding.toLowerCase()=="mtom") { channels.push(new MtomMessageEncodingBindingElement({MessageVersion: "Soap11" })) } else { channels.push(new TextMessageEncodingBindingElement({MessageVersion: "Soap11" })) } if (this.SecurityMode=="TransportWithMessageCredential" && this.MessageClientCredentialType == "UserName") { channels.push(new SecurityBindingElement({ AuthenticationMode: "UserNameOverTransport"})); } else if (this.SecurityMode=="Message" && this.MessageClientCredentialType == "Certificate") { channels.push(new SecurityBindingElement({ AuthenticationMode: "MutualCertificate"})); } if (this.SecurityMode=="TransportWithMessageCredential") { channels.push(new HttpsTransportBindingElement()); } else { channels.push(new HttpTransportBindingElement) } this.customBinding = new CustomBinding(channels, []) return this.customBinding } this.getHandlers = function(proxy) { var custom = this.getCustomBinding() return custom.getHandlers(proxy) } } function WSHttpBinding(options) { utils.unifyProperties(options, this) this.getCustomBinding = function() { if (this.customBinding) return this.customBinding var channels = []; if (this.MessageEncoding && this.MessageEncoding.toLowerCase()=="mtom") { channels.push(new MtomMessageEncodingBindingElement( {MessageVersion: "Soap12WSAddressing10" })) } else { channels.push(new TextMessageEncodingBindingElement({ MessageVersion: "Soap12WSAddressing10" })) } if (this.SecurityMode=="TransportWithMessageCredential" && this.MessageClientCredentialType == "UserName") { channels.push(new SecurityBindingElement({ AuthenticationMode: "UserNameOverTransport"})) } else if (this.SecurityMode=="Message" && this.MessageClientCredentialType == "Certificate") { channels.push(new SecurityBindingElement({ AuthenticationMode: "MutualCertificate"})); } if (this.SecurityMode=="TransportWithMessageCredential") { channels.push(new HttpsTransportBindingElement()) } else { channels.push(new HttpTransportBindingElement) } this.customBinding = new CustomBinding(channels, []) return this.customBinding } this.getHandlers = function(proxy) { var custom = this.getCustomBinding() return custom.getHandlers(proxy) } } function CustomBinding(channels, options) { this.channels = channels utils.unifyProperties(options, this) this.getCustomBinding = function() { return this; } this.getContentType = function() { for (var i in this.channels) { if (this.channels[i].MessageVersion && this.channels[i].MessageVersion.indexOf("Soap12")!=-1) { return "application/soap+xml" } } return "text/xml"; } this.getHandlers = function(proxy) { var handlers = []; for (var i in channels) { this.channels[i].process(handlers, proxy, channels) } return handlers } } function AddAddressing(addr, handlers) { if (!addr) return var v = messageVersionToAddressingVersion(addr) //we unshift and not pop. wsa channel must be first so it is before security. //otherwise security cannot sign the wsa. if (v) handlers.unshift(new ws.Addr(v)) } function messageVersionToAddressingVersion(version) { if (version.indexOf("WSAddressingAugust2004")!=-1) { return AddressingMap["WSAddressingAugust2004"] } if (version.indexOf("WSAddressing10")!=-1) { return AddressingMap["WSAddressing10"] } return null } function MtomMessageEncodingBindingElement(options) { //set defaults this.MessageVersion = "Soap12WSAddressing10" utils.unifyProperties(options, this) this.process = function(handlers, proxy, wcfChannels) { AddAddressing(this.MessageVersion, handlers) handlers.push(new ws.Mtom()) } } function TextMessageEncodingBindingElement(options) { //set defaults this.MessageVersion = "Soap11" utils.unifyProperties(options, this) this.process = function(handlers, proxy, wcfChannels) { AddAddressing(this.MessageVersion, handlers) } } function HttpTransportBindingElement(options) { this.process = function(handlers, proxy, wcfChannels) { handlers.push(new ws.Http()); } } function HttpsTransportBindingElement(options) { this.process = function(handlers, proxy, wcfChannels) { handlers.push(new ws.Http()) } } function FixedKeyInfo(key) { this.getKey = function(keyInfo) { return key } } function SecurityBindingElement(options) { utils.unifyProperties(options, this) if (this.IncludeTimestamp==undefined) this.IncludeTimestamp = true if (this.ValidateResponseSignature==undefined) this.ValidateResponseSignature = false this.process = function(handlers, proxy, wcfChannels) { var tokens = []; var sec = new ws.Security() if (this.AuthenticationMode == "UserNameOverTransport") { tokens = [new ws.UsernameToken({ username: proxy.ClientCredentials.Username.Username, password: proxy.ClientCredentials.Username.Password})] } if (this.AuthenticationMode == "MutualCertificate") { var x509 = new ws.X509BinarySecurityToken( { "key": proxy.ClientCredentials.ClientCertificate.Certificate}) var signature = new ws.Signature(x509) this.addPrimarySignatureReferences(signature, wcfChannels) if (this.ValidateResponseSignature) { sec.options.responseKeyInfoProvider = new FixedKeyInfo( proxy.ClientCredentials.ServiceCertificate.DefaultCertificate) } tokens = [x509, signature] } sec.tokens = tokens if (!this.IncludeTimestamp) sec.options.excludeTimestamp = true handlers.push(sec) } this.addPrimarySignatureReferences = function(signature, wcfChannels) { signature.addReference("//*[local-name(.)='Body']") if (this.IncludeTimestamp) signature.addReference("//*[local-name(.)='Timestamp']") var wsa = this.getWsaVersion(wcfChannels) if (wsa) signature.addReference("//*[namespace-uri(.)='" + wsa + "' and local-name(.)!='Address']") } this.getWsaVersion = function(wcfChannels) { for (var c in wcfChannels) { var v = wcfChannels[c].MessageVersion if (v) return messageVersionToAddressingVersion(v) } return null } } var AddressingMap = { "WSAddressingAugust2004": "http://schemas.xmlsoap.org/ws/2004/08/addressing" , "WSAddressing10": "http://www.w3.org/2005/08/addressing" }