ws-js-public
Version:
WS-* original repository https://github.com/yaronn/ws.js.git, An update was made where the xmldom dependency was changed to @xmldom/xmldom
177 lines (145 loc) • 6.47 kB
JavaScript
var select = require('../../../xpath').SelectNodes
, Dom = require('@xmldom/xmldom').DOMParser
, utils = require('../../../utils')
, consts = require('../../../consts')
, dateFormat = require('dateformat')
, SignedXml = require('xml-crypto').SignedXml
, fs = require('fs')
exports.SecurityClientHandler = SecurityClientHandler
exports.UsernameToken = UsernameToken
exports.X509BinarySecurityToken = X509BinarySecurityToken
var BEGIN_CERT = "-----BEGIN CERTIFICATE-----"
var END_CERT = "-----END CERTIFICATE-----"
function SecurityClientHandler(options, tokens) {
this.options = options || {}
this.options.excludeTimestamp = this.options.excludeTimestamp || false
this.options.responseKeyInfoProvider = this.options.responseKeyInfoProvider || null
this.options.validateResponseSignature = this.options.validateResponseSignature || false
this.tokens = tokens || []
this.id = 0
}
SecurityClientHandler.prototype.send = function(ctx, callback) {
var self = this
var doc = new Dom().parseFromString(ctx.request)
this.AddNamespaces(doc)
var header = select(doc, "/*[local-name(.)='Envelope']/*[local-name(.)='Header']")[0]
, security = utils.appendElement(doc, header, consts.security_ns, "o:Security", null)
if (!this.options.excludeTimestamp)
this.AddTimestamp(doc, security)
for (var i in this.tokens) {
doc = this.tokens[i].applyMe(doc, this)
}
ctx.request = doc.toString()
this.next.send(ctx, function(ctx) {
self.receive(ctx, callback)
})
}
SecurityClientHandler.prototype.AddNamespaces = function(doc) {
doc.documentElement.setAttribute("xmlns:u", consts.security_utility_ns)
doc.documentElement.setAttribute("xmlns:o", consts.security_ns)
}
SecurityClientHandler.prototype.AddTimestamp = function(doc, security) {
var timestamp = utils.appendElement(doc, security, consts.security_utility_ns, "u:Timestamp", null)
, created_time = new Date()
, expires_time = new Date(created_time)
, expires_timespan = 5
expires_time.setMinutes ( created_time.getMinutes() + expires_timespan )
var created = utils.appendElement(doc, timestamp, consts.security_utility_ns, "u:Created", dateFormat(created_time, "isoUtcDateTime"))
, expires = utils.appendElement(doc, timestamp, consts.security_utility_ns, "u:Expires", dateFormat(expires_time, "isoUtcDateTime"))
}
SecurityClientHandler.prototype.receive = function(ctx, callback) {
//fs.writeFileSync("c:\\temp\\resp.xml", ctx.response.toString())
if (this.options.validateResponseSignature) {
var sig = new SignatureValidator(this.options.responseKeyInfoProvider)
sig.validate(ctx.response.toString())
}
callback(ctx)
}
SecurityClientHandler.prototype.getNextId = function() {
return "sec_" + this.id++
}
function SignatureValidator(keyInfoProvider) {
this.keyInfoProvider = keyInfoProvider
this.validate = function(soap) {
var doc = new Dom().parseFromString(soap)
var nodes = select(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")
if (nodes.length==0)
return
var signature = nodes[0]
var sig = new SignedXml("wssecurity")
sig.keyInfoProvider = this.keyInfoProvider
sig.loadSignature(signature.toString())
var res = sig.checkSignature(soap)
if (!res) {
console.log("signature not valid: " + sig.validationErrors)
throw new Error("signature not valid: " + sig.validationErrors)
}
}
}
/*
function WssKeyInfo(soap) {
this.soap = soap
this.getKeyInfo = function(key) {
return ""
}
this.getKey = function(keyInfo) {
var doc = new Dom().parseFromString(keyInfo)
var nodes = select(doc, "//@URI")
if (nodes.length==0)
throw new Error("could not find key in KeyInfo to use for validation")
var uri = nodes[0]
var xpath = "//*['" + uri + "' = @*[local-name(.)='Id' and namespace-uri(.)='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd']]"
var nodes = select(doc, xpath)
if (nodes.length==0)
throw new Error("can not validate signature: could not find binary security token with id " + uri)
return BEGIN_CERT + nodes[0].data + END_CERT
}
}
*/
function UsernameToken(options) {
this.options = options
}
UsernameToken.prototype.applyMe = function(doc, security) {
var security = select(doc,
"/*[local-name(.)='Envelope']/*[local-name(.)='Header']/*[local-name(.)='Security']")[0]
, token = utils.appendElement(doc, security, security.security_ns, "o:UsernameToken", null)
, username = utils.appendElement(doc, token, security.security_ns, "o:Username",
this.options.username)
, password = utils.appendElement(doc, token, security.security_ns, "o:Password",
this.options.password)
password.setAttribute("Type",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText")
return doc
}
function X509BinarySecurityToken(options) {
this.options = options
if (!this.options.key)
throw new Error("Cannot create an X509Token token without specifying a key in the options")
}
X509BinarySecurityToken.prototype.getKey = function() {
return this.options.key
}
X509BinarySecurityToken.prototype.getId = function() {
return this.id
}
X509BinarySecurityToken.prototype.extractBase64Key = function(key) {
if (key.indexOf(BEGIN_CERT) === -1 || key.indexOf(END_CERT) === -1) throw new Error("Provided PEM-format key does not contain certificate information! Try appending the certificate to the end of the file.");
var start = key.indexOf(BEGIN_CERT) + BEGIN_CERT.length
var end = key.indexOf(END_CERT)
var res = key.substring(start, end)
res = res.replace(/(\r\n|\n|\r)/gm,"")
return res
}
X509BinarySecurityToken.prototype.applyMe = function(doc, security) {
this.id = security.getNextId()
var base64Key = this.extractBase64Key(this.options.key)
var security = select(doc,
"/*[local-name(.)='Envelope']/*[local-name(.)='Header']/*[local-name(.)='Security']")[0]
, token = utils.appendElement(doc, security, security.security_ns, "o:BinarySecurityToken", base64Key)
token.setAttribute("ValueType",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3")
token.setAttribute("EncodingType",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary")
token.setAttribute("u:Id", this.id)
return doc
}