Skip to content

Commit

Permalink
Implemented optional asynchronous authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
schantaraud authored and jsdevel committed May 4, 2018
1 parent c71f44c commit 9ea32e2
Show file tree
Hide file tree
Showing 3 changed files with 296 additions and 82 deletions.
20 changes: 20 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,26 @@ To change the HTTP statusCode of the response include it on the fault. The stat

If `server.authenticate` is not defined then no authentication will take place.

Asynchronous authentication:
``` javascript
server = soap.listen(...)
server.authenticate = function(security, callback) {
var created, nonce, password, user, token;
token = security.UsernameToken, user = token.Username,
password = token.Password, nonce = token.Nonce, created = token.Created;

myDatabase.getUser(user, function (err, dbUser) {
if (err || !dbUser) {
callback(false);
return;
}

callback(password === soap.passwordDigest(nonce, created, dbUser.password));
});
};
```

Synchronous authentication:
``` javascript
server = soap.listen(...)
server.authenticate = function(security) {
Expand Down
210 changes: 128 additions & 82 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,103 +237,149 @@ Server.prototype._process = function (input, req, callback) {
bindings = this.wsdl.definitions.bindings, binding,
method, methodName,
serviceName, portName,
includeTimestamp = obj.Header && obj.Header.Security && obj.Header.Security.Timestamp;
includeTimestamp = obj.Header && obj.Header.Security && obj.Header.Security.Timestamp,
authenticate = self.authenticate || function defaultAuthenticate() { return true; };

if (typeof self.authenticate === 'function') {
if (!obj.Header || !obj.Header.Security) {
throw new Error('No security header');
}
if (!self.authenticate(obj.Header.Security)) {
throw new Error('Invalid username or password');
function process() {

if (typeof self.log === 'function') {
self.log("info", "Attempting to bind to " + pathname);
}
}

if (typeof self.log === 'function') {
self.log("info", "Attempting to bind to " + pathname);
}
//Avoid Cannot convert undefined or null to object due to Object.keys(body)
//and throw more meaningful error
if (!body) {
throw new Error('Failed to parse the SOAP Message body');
}

//Avoid Cannot convert undefined or null to object due to Object.keys(body)
//and throw more meaningful error
if (!body) {
throw new Error('Failed to parse the SOAP Message body');
}
// use port.location and current url to find the right binding
binding = (function () {
var services = self.wsdl.definitions.services;
var firstPort;
var name;
for (name in services) {
serviceName = name;
var service = services[serviceName];
var ports = service.ports;
for (name in ports) {
portName = name;
var port = ports[portName];
var portPathname = url.parse(port.location).pathname.replace(/\/$/, '');

if (typeof self.log === 'function') {
self.log("info", "Trying " + portName + " from path " + portPathname);
}

// use port.location and current url to find the right binding
binding = (function (self) {
var services = self.wsdl.definitions.services;
var firstPort;
var name;
for (name in services) {
serviceName = name;
var service = services[serviceName];
var ports = service.ports;
for (name in ports) {
portName = name;
var port = ports[portName];
var portPathname = url.parse(port.location).pathname.replace(/\/$/, '');
if (portPathname === pathname)
return port.binding;

if (typeof self.log === 'function') {
self.log("info", "Trying " + portName + " from path " + portPathname);
// The port path is almost always wrong for generated WSDLs
if (!firstPort) {
firstPort = port;
}
}
}
return !firstPort ? void 0 : firstPort.binding;
})();

if (portPathname === pathname)
return port.binding;
if (!binding) {
throw new Error('Failed to bind to WSDL');
}

// The port path is almost always wrong for generated WSDLs
if (!firstPort) {
firstPort = port;
}
try {
if (binding.style === 'rpc') {
methodName = Object.keys(body)[0];

self.emit('request', obj, methodName);
if (headers)
self.emit('headers', headers, methodName);

self._executeMethod({
serviceName: serviceName,
portName: portName,
methodName: methodName,
outputName: methodName + 'Response',
args: body[methodName],
headers: headers,
style: 'rpc'
}, req, callback);
} else {
var messageElemName = (Object.keys(body)[0] === 'attributes' ? Object.keys(body)[1] : Object.keys(body)[0]);
var pair = binding.topElements[messageElemName];

self.emit('request', obj, pair.methodName);
if (headers)
self.emit('headers', headers, pair.methodName);

self._executeMethod({
serviceName: serviceName,
portName: portName,
methodName: pair.methodName,
outputName: pair.outputName,
args: body[messageElemName],
headers: headers,
style: 'document'
}, req, callback, includeTimestamp);
}
}
return !firstPort ? void 0 : firstPort.binding;
})(this);

if (!binding) {
throw new Error('Failed to bind to WSDL');
}
catch (error) {
if (error.Fault !== undefined) {
return self._sendError(error.Fault, callback, includeTimestamp);
}

try {
if (binding.style === 'rpc') {
methodName = Object.keys(body)[0];

self.emit('request', obj, methodName);
if (headers)
self.emit('headers', headers, methodName);

self._executeMethod({
serviceName: serviceName,
portName: portName,
methodName: methodName,
outputName: methodName + 'Response',
args: body[methodName],
headers: headers,
style: 'rpc'
}, req, callback);
} else {
var messageElemName = (Object.keys(body)[0] === 'attributes' ? Object.keys(body)[1] : Object.keys(body)[0]);
var pair = binding.topElements[messageElemName];

self.emit('request', obj, pair.methodName);
if (headers)
self.emit('headers', headers, pair.methodName);

self._executeMethod({
serviceName: serviceName,
portName: portName,
methodName: pair.methodName,
outputName: pair.outputName,
args: body[messageElemName],
headers: headers,
style: 'document'
}, req, callback, includeTimestamp);
throw error;
}
}
catch (error) {
if (error.Fault !== undefined) {
return self._sendError(error.Fault, callback, includeTimestamp);
}

throw error;
// Authentication

if (typeof authenticate === 'function') {

var authResultProcessed = false,
processAuthResult = function (authResult) {

if (!authResultProcessed && (authResult || authResult === false)) {

authResultProcessed = true;

if (authResult) {

try {
process();
} catch (error) {

if (error.Fault !== undefined) {
return self._sendError(error.Fault, callback, includeTimestamp);
}

return self._sendError({
Code: {
Value: 'SOAP-ENV:Server',
Subcode: { value: 'InternalServerError' }
},
Reason: { Text: error.toString() },
statusCode: 500
}, callback, includeTimestamp);
}

} else {

return self._sendError({
Code: {
Value: 'SOAP-ENV:Client',
Subcode: { value: 'AuthenticationFailure' }
},
Reason: { Text: 'Invalid username or password' },
statusCode: 401
}, callback, includeTimestamp);
}
}
};

processAuthResult(authenticate(obj.Header && obj.Header.Security, processAuthResult));

} else {
throw new Error('Invalid authenticate function (not a function)');
}
};

Expand Down
Loading

0 comments on commit 9ea32e2

Please # to comment.