diff --git a/lib/cas.js b/lib/cas.js
index 3a49580..55f6f86 100644
--- a/lib/cas.js
+++ b/lib/cas.js
@@ -18,6 +18,8 @@ var http = require('http');
var https = require('https');
var url = require('url');
var cheerio = require('cheerio');
+var parseXML = require('xml2js').parseString;
+var XMLprocessors = require('xml2js/lib/processors');
@@ -25,27 +27,27 @@ var cheerio = require('cheerio');
* Initialize CAS with the given `options`.
*
* @param {Object} options
- * {
- * 'base_url':
+ * {
+ * 'base_url':
* The full URL to the CAS server, including the base path.
- * 'service':
+ * 'service':
* The URL of the page being authenticated. Can be omitted here and
* specified during validate(). Or detected automatically during
* authenticate().
- * 'version':
+ * 'version':
* Either 1.0 or 2.0
*
*
* 'external_pgt_url':
* (optional) The URL of the PGT callback server.
* e.g. https://callback.example.com:8989/
- * The CAS server will try to contact this host every time a user
- * logs in.
+ * The CAS server will try to contact this host every time a user
+ * logs in.
* Do not use with the `pgt_server` option.
*
*
* 'pgt_server': (previously 'proxy_server')
- * (optional) Set to TRUE if you want to automatically start
+ * (optional) Set to TRUE if you want to automatically start
* a PGT callback server internally.
* Can be used to create a standalone PGT callback server.
* Do not combine with `external_pgt_url`.
@@ -62,13 +64,13 @@ var cheerio = require('cheerio');
*
*
* 'ssl_key': (previously 'proxy_server_key')
- * A string value of your SSL private key.
+ * A string value of your SSL private key.
* Required for `pgt_server`.
* Optional for `external_pgt_url`.
* Not needed otherwise.
*
* 'ssl_cert': (previously 'proxy_server_cert')
- * A string value of your SSL certificate.
+ * A string value of your SSL certificate.
* Required for `pgt_server`.
* Optional for `external_pgt_url`.
* Not needed otherwise.
@@ -87,10 +89,10 @@ var cheerio = require('cheerio');
* }
* @api public
*/
-var CAS = module.exports = function CAS(options)
+var CAS = module.exports = function CAS(options)
{
options = options || {};
-
+
// Backwards compatibility for old option names
options.pgt_server = options.pgt_server || options.proxy_server;
options.pgt_host = options.pgt_host || options.proxy_callback_host;
@@ -101,16 +103,16 @@ var CAS = module.exports = function CAS(options)
if (!options.base_url) {
throw new Error('Required CAS option `base_url` missing.');
- }
+ }
var cas_url = url.parse(options.base_url);
if (cas_url.protocol != 'https:') {
throw new Error('Only https CAS servers are supported.');
- }
+ }
if (!cas_url.hostname) {
throw new Error('Option `base_url` must be a valid url like: https://example.com/cas');
- }
-
+ }
+
this.version = options.version || 1.0;
this.hostname = cas_url.hostname;
this.port = cas_url.port || 443;
@@ -118,17 +120,17 @@ var CAS = module.exports = function CAS(options)
this.service = options.service;
this.pgtStore = {};
this.pgt_is_external = false;
-
+
// SSL options used when running a PGT callback server,
// or as a client contacting the external PGT URL.
this.ssl_cert = options.ssl_cert || null;
this.ssl_key = options.ssl_key || null;
this.ssl_ca = options.ssl_ca || null;
-
+
// Setting this to false will allow cause bad SSL certificates to still
// be accepted. Use only for testing.
this.secureSSL = true;
-
+
// Optional single sign out server list
if (options.sso_servers) {
this.ssoServers = options.sso_servers;
@@ -145,7 +147,7 @@ var CAS = module.exports = function CAS(options)
}
this.is_pgt_external = true;
this.pgt_url = url.format(pgt_url);
-
+
// Deprecated
if (options.external_proxy_url) {
var proxy_url = url.parse(options.external_proxy_url);
@@ -169,7 +171,7 @@ var CAS = module.exports = function CAS(options)
//// Optional
this.proxy_server_port = options.proxy_server_port || 0; // deprecated
this.pgt_port = options.pgt_port || 80443
-
+
this.startPgtServer(this.ssl_key, this.ssl_cert, this.ssl_ca, this.pgt_host, this.pgt_port, this.proxy_server_port);
}
@@ -185,7 +187,7 @@ CAS.version = '0.0.5';
/**
- * Force CAS authentication on a web page. If users are not yet authenticated,
+ * Force CAS authentication on a web page. If users are not yet authenticated,
* they will be redirected to the CAS server to log in there.
*
* @param {object} req
@@ -195,7 +197,7 @@ CAS.version = '0.0.5';
* @param {function} callback
* callback(err, status, username, extended)
* @param {String} service
- * (optional) The URL of the service/page that requires authentication.
+ * (optional) The URL of the service/page that requires authentication.
* Default is to extract this automatically from
* the `req` object.
* @api public
@@ -204,7 +206,7 @@ CAS.prototype.authenticate = function(req, res, callback, service)
{
var casURL = 'https://' + this.hostname + ':' + this.port + this.base_path;
var reqURL = url.parse(req.url, true);
-
+
// Try to extract the CAS ticket from the URL
var ticket = reqURL.query['ticket'];
@@ -219,7 +221,7 @@ CAS.prototype.authenticate = function(req, res, callback, service)
query: reqURL.query
});
}
-
+
// No ticket, so we haven't been sent to the CAS server yet
if (!ticket) {
// redirect to CAS server now
@@ -229,7 +231,7 @@ CAS.prototype.authenticate = function(req, res, callback, service)
res.end();
}
- // We have a ticket!
+ // We have a ticket!
else {
// Validate it with the CAS server now
this.validate(ticket, callback, service);
@@ -239,7 +241,7 @@ CAS.prototype.authenticate = function(req, res, callback, service)
/**
- * Handle a single sign-out request from the CAS server.
+ * Handle a single sign-out request from the CAS server.
*
* In CAS 3.x the server keeps track of all the `ticket` and `service` values
* associated with each user. Then when the user logs out from one site, the
@@ -247,11 +249,11 @@ CAS.prototype.authenticate = function(req, res, callback, service)
* a sign-out request containing the original `ticket` used to login.
*
* This is optional. But if you do use this, it must come before authenticate().
- * Also, it will only work if the service is accessible on the network by the
+ * Also, it will only work if the service is accessible on the network by the
* CAS server.
*
- * Unlike the other functions in this module, this one will only work
- * with Express or something else that pre-processes the body of a POST
+ * Unlike the other functions in this module, this one will only work
+ * with Express or something else that pre-processes the body of a POST
* request. It is not compatible with basic node.js http req objects.
*
* @param {Object} req
@@ -273,10 +275,10 @@ CAS.prototype.handleSingleSignout = function(req, res, next, logoutCallback)
// not a recognized single signout server
return next();
}
-
+
try {
// This was a signout request. Parse the XML.
- var $ = cheerio.load(req.body['logoutRequest']);
+ var $ = cheerio.load(req.body['logoutRequest'], {xmlMode: true});
var ticketElems = $('samlp\\:SessionIndex');
if (ticketElems && ticketElems.length > 0) {
// This is the ticket that was issued by CAS when the user
@@ -310,8 +312,8 @@ CAS.prototype.handleSingleSignout = function(req, res, next, logoutCallback)
* @param {String} returnUrl
* (optional) The URL that the user will return to after logging out.
* @param {Boolean} doRedirect
- * (optional) Set this to TRUE to have the CAS server redirect the user
- * automatically. Default is for the CAS server to only provide a
+ * (optional) Set this to TRUE to have the CAS server redirect the user
+ * automatically. Default is for the CAS server to only provide a
* hyperlink to be clicked on.
* @api public
*/
@@ -328,7 +330,7 @@ CAS.prototype.logout = function(req, res, returnUrl, doRedirect)
// Logout with no way back
logout_path = '/logout';
}
-
+
var redirectURL = 'https://' + this.hostname + ':' + this.port + this.base_path + logout_path;
res.writeHead(307, {'Location' : redirectURL});
res.write('CAS logout');
@@ -354,32 +356,43 @@ CAS.prototype.logout = function(req, res, returnUrl, doRedirect)
* @param {String} service
* The URL of the service requesting authentication. Optional if
* the `service` option was already specified during initialization.
- * @param {Boolean} renew
+ * @param {Boolean} renew
* (optional) Set this to TRUE to force the CAS server to request
* credentials from the user even if they had already done so
* recently.
* @api public
*/
-CAS.prototype.validate = function(ticket, callback, service, renew)
+CAS.prototype.validate = function(ticket, callback, service, renew)
{
// Use different CAS path depending on version
var validate_path;
var pgtURL;
var cas_version = this.version;
- if (this.version < 2.0) {
- // CAS 1.0
- validate_path = 'validate';
- } else {
- // CAS 2.0
- pgtURL = this.pgt_url;
- if (ticket.indexOf('PT-') == 0) {
- validate_path = 'proxyValidate';
- } else {
- //validate_path = 'serviceValidate';
- validate_path = 'proxyValidate';
- }
+
+ var requestOptions = {
+ host: this.hostname,
+ port: this.port,
+ ca: this.ssl_ca || null,
+ rejectUnauthorized: this.secureSSL
+ };
+
+ var validateUri;
+ switch(cas_version){
+ case '1.0':
+ validateUri = '/validate'; break;
+ case '2.0':
+ validateUri = '/serviceValidate'; break;
+ case '3.0':
+ validateUri = '/p3/serviceValidate'; break;
+ case 'saml1.1':
+ validateUri = '/samlValidate'; break;
}
-
+
+ // CAS version check
+ if (!validateUri) {
+ throw new Error('Incorrect CAS Version.');
+ }
+
// Service URL can be specified in the function call, or during
// initialization.
var service_url = service || this.service;
@@ -387,29 +400,53 @@ CAS.prototype.validate = function(ticket, callback, service, renew)
throw new Error('Required CAS option `service` missing.');
}
- var query = {
- 'ticket': ticket,
- 'service': service_url
- };
- if (renew) {
- query['renew'] = 1;
- }
- if (pgtURL) {
- query['pgtUrl'] = pgtURL;
- }
-
- var queryPath = url.format({
- pathname: this.base_path+'/'+validate_path,
+ if (['1.0', '2.0', '3.0'].indexOf(cas_version) >= 0) {
+ var query = {
+ 'ticket': ticket,
+ 'service': service_url
+ };
+ if (renew) {
+ query['renew'] = 1;
+ }
+ if (pgtURL) {
+ query['pgtUrl'] = pgtURL;
+ }
+ requestOptions.method = 'GET';
+ requestOptions.path = url.format({
+ pathname: this.base_path + validateUri,
query: query
});
+ } else if (cas_version === 'saml1.1') {
+ var now = new Date();
+ var post_data = '\n' +
+ '\n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ ' ' + ticket + '\n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ '';
- var req = https.get({
- host: this.hostname,
- port: this.port,
- path: queryPath,
- ca: this.ssl_ca || null,
- rejectUnauthorized: this.secureSSL
- }, function(res) {
+ requestOptions.method = 'POST';
+ requestOptions.path = url.format({
+ pathname: this.base_path + validateUri,
+ query: {
+ TARGET: service_url,
+ ticket: ''
+ }
+ });
+ requestOptions.headers = {
+ 'Content-Type': 'text/xml',
+ 'Content-Length': Buffer.byteLength(post_data)
+ };
+ }
+
+ var req = https.request(requestOptions, function(res) {
// Handle server errors
res.on('error', function(e) {
callback(e);
@@ -426,8 +463,9 @@ CAS.prototype.validate = function(ticket, callback, service, renew)
});
res.on('end', function() {
+ // console.log('cas validate response', response);
// CAS 1.0
- if (cas_version < 2.0) {
+ if (cas_version == '1.0') {
var sections = response.split('\n');
if (sections.length >= 1) {
if (sections[0] == 'no') {
@@ -440,13 +478,59 @@ CAS.prototype.validate = function(ticket, callback, service, renew)
}
// Format was not correct, error
callback(new Error('Bad response format.'));
- }
-
+ }
+
+ else if (cas_version == 'saml1.1'){
+ parseXML(response, {
+ trim: true,
+ normalize: true,
+ explicitArray: false,
+ tagNameProcessors: [XMLprocessors.normalize, XMLprocessors.stripPrefix]
+ }, function(err, result) {
+ if (err) {
+ return callback(new Error('Response from CAS server was bad.'));
+ }
+ try {
+ var samlResponse = result.envelope.body.response;
+ var success = samlResponse.status.statuscode.$.Value.split(':')[1];
+ if (success !== 'Success') {
+ return callback(new Error('CAS authentication failed (' + success + ').'));
+ } else {
+ var attributes = {};
+ var attributesArray = samlResponse.assertion.attributestatement.attribute;
+ if (!(attributesArray instanceof Array)) {
+ attributesArray = [attributesArray];
+ }
+ attributesArray.forEach(function(attr) {
+ var thisAttrValue;
+ if (attr.attributevalue instanceof Array) {
+ thisAttrValue = [];
+ attr.attributevalue.forEach(function(v) {
+ thisAttrValue.push(v._);
+ });
+ } else {
+ thisAttrValue = attr.attributevalue._;
+ }
+ attributes[attr.$.AttributeName] = thisAttrValue;
+ });
+ return callback(null, true, samlResponse.assertion.authenticationstatement.subject.nameidentifier, {
+ attributes: attributes,
+ username: samlResponse.assertion.authenticationstatement.subject.nameidentifier,
+ ticket: ticket
+ });
+ }
+ } catch (err) {
+ console.log(err);
+ return callback(new Error('CAS authentication failed.'), false);
+ }
+ });
+ }
+
// CAS 2.0 (XML response, and extended attributes)
else {
// Use cheerio to parse the XML repsonse.
- var $ = cheerio.load(response);
-
+ var $ = cheerio.load(response, {xmlMode: true});
+
// Check for auth success
var elemSuccess = $('cas\\:authenticationSuccess').first();
if (elemSuccess && elemSuccess.length > 0) {
@@ -459,14 +543,14 @@ CAS.prototype.validate = function(ticket, callback, service, renew)
// Got username
var username = elemUser.text();
-
+
// Look for optional proxy granting ticket
var pgtIOU;
var elemPGT = elemSuccess.find('cas\\:proxyGrantingTicket').first();
if (elemPGT) {
pgtIOU = elemPGT.text();
}
-
+
// Look for optional proxies
var proxies = [];
var elemProxies = elemSuccess.find('cas\\:proxies');
@@ -477,7 +561,7 @@ CAS.prototype.validate = function(ticket, callback, service, renew)
// Look for optional attributes
var attributes = parseAttributes(elemSuccess);
-
+
callback(undefined, true, username, {
'username': username,
'attributes': attributes,
@@ -505,12 +589,17 @@ CAS.prototype.validate = function(ticket, callback, service, renew)
};
});
});
-
+
// Connection error with the CAS server
req.on('error', function(err) {
callback(err);
req.abort();
});
+
+ if (cas_version === 'saml1.1') {
+ req.write(post_data);
+ }
+ req.end();
};
@@ -530,7 +619,7 @@ CAS.prototype.validate = function(ticket, callback, service, renew)
CAS.prototype.getProxyGrantingTicket = function(pgtIOU, callback)
{
var pgt = '';
-
+
// If configured for external PGT server use, fetch the PT from there
if (this.is_pgt_external) {
var urlFetchPGT = url.parse(this.pgt_url + 'getPGT?pgtiou=' + pgtIOU);
@@ -538,7 +627,7 @@ CAS.prototype.getProxyGrantingTicket = function(pgtIOU, callback)
urlFetchPGT.cert = this.ssl_cert || null;
urlFetchPGT.ca = this.ssl_ca || null;
urlFetchPGT.rejectUnauthorized = this.secureSSL;
-
+
var req = https.get(urlFetchPGT, function(res) {
res.on('data', function(chunk) {
pgt += chunk;
@@ -553,7 +642,7 @@ CAS.prototype.getProxyGrantingTicket = function(pgtIOU, callback)
callback(err);
});
});
-
+
// Error starting the connection to the PGT server
req.on('error', function(err) {
callback(err);
@@ -561,7 +650,7 @@ CAS.prototype.getProxyGrantingTicket = function(pgtIOU, callback)
});
return null;
}
-
+
// Look up the PGT locally
else if (this.pgtStore[pgtIOU]) {
pgt = this.pgtStore[pgtIOU]['pgtID'];
@@ -596,10 +685,10 @@ CAS.prototype.getProxyGrantingTicket = function(pgtIOU, callback)
* callback(err, pt)
* @api public
*/
-CAS.prototype.getProxyTicket = function(pgtIOU, targetService, callback)
+CAS.prototype.getProxyTicket = function(pgtIOU, targetService, callback)
{
var self = this;
-
+
// Obtain the PGT
this.getProxyGrantingTicket(pgtIOU, function(err, pgt) {
if (err) {
@@ -615,7 +704,7 @@ CAS.prototype.getProxyTicket = function(pgtIOU, targetService, callback)
rejectUnauthorized: self.secureSSL,
path: url.format({
pathname: self.base_path + '/proxy',
- query: {
+ query: {
'targetService': targetService,
'pgt': pgt
}
@@ -625,20 +714,20 @@ CAS.prototype.getProxyTicket = function(pgtIOU, targetService, callback)
res.on('error', function(e) {
callback(e);
});
-
+
// Read result
res.setEncoding('utf8');
var response = '';
res.on('data', function(chunk) {
response += chunk;
if (response.length > 1e6) {
- req.connection.destroy();
+ req.connection.destroy();
}
});
res.on('end', function() {
// Use cheerio to parse the XML response
- var $ = cheerio.load(response);
-
+ var $ = cheerio.load(response, {xmlMode: true});
+
// Got the proxy ticket
var elemTicket = $('cas\\:proxyTicket').first();
if (elemTicket && elemTicket.length > 0) {
@@ -672,8 +761,8 @@ CAS.prototype.getProxyTicket = function(pgtIOU, targetService, callback)
/**
* Start a PGT callback server.
- *
- * This is a local HTTPS server that listens for incoming connections from
+ *
+ * This is a local HTTPS server that listens for incoming connections from
* the CAS server. Any PGTs received from the CAS server will be stored
* in `this.pgtStore`.
*
@@ -683,9 +772,9 @@ CAS.prototype.getProxyTicket = function(pgtIOU, targetService, callback)
*
* The following functionality is deprecated:
*
- * This is optionally also an http proxy server that listens for outgoing
- * requests from clients that already have a PGTIOU. In addition to the
- * normal HTTP information, the client must also supply these two headers
+ * This is optionally also an http proxy server that listens for outgoing
+ * requests from clients that already have a PGTIOU. In addition to the
+ * normal HTTP information, the client must also supply these two headers
* in the request:
* cas-proxy-pgtiou
* cas-proxy-targeturl
@@ -708,7 +797,7 @@ CAS.prototype.getProxyTicket = function(pgtIOU, targetService, callback)
* internal requests via CAS.proxiedRequest().
* @api public
*/
-CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPort, proxyPort)
+CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPort, proxyPort)
{
var serverOptions = {
'key': key,
@@ -716,17 +805,17 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
'ca': ca
};
var self = this;
-
+
// This is the pgtURL that will be sent to the CAS server during a
// validation request. The CAS server will try to connect to it.
this.pgt_url = 'https://' + callbackHost + ':' + callbackPort + '/';
-
+
// PGT callback server that listens for incoming connections from
// the CAS server.
var pgtServer = https.createServer(serverOptions);
console.log('Starting PGT callback server on port ' + callbackPort);
pgtServer.addListener("request", function(req, res) {
-
+
var reqURL = url.parse(req.url, true);
// Check if this is a request from a CAS _client_ to get a PGT.
@@ -745,7 +834,7 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
}
return;
}
-
+
// Otherwise this is a connection from the CAS _server_.
// The incoming connection tells us what the PGTIOU and PGT values
// are. It expects only a HTTP 200 response in return.
@@ -765,7 +854,7 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
}
});
pgtServer.listen(callbackPort);
-
+
// Start an interval for garbage collection of the local PGT store.
if (this.pgtInterval) {
clearInterval(this.pgtInterval);
@@ -780,8 +869,8 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
}
}
}, 1000 * 60);
-
-
+
+
// Deprecated:
// Proxy server that listens for HTTPS connections from other CAS clients
// and forwards them to the target service. Disabled by default.
@@ -789,7 +878,7 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
var proxyServer = https.createServer(serverOptions);
console.log('Starting CAS-aware HTTP proxy server on port ' + proxyPort);
proxyServer.addListener("request", function(req, res) {
- // Use "cas-proxy-..." headers to obtain information about the
+ // Use "cas-proxy-..." headers to obtain information about the
// requested target service.
try {
var pgtIOU = req.headers['cas-proxy-pgtiou'];
@@ -818,7 +907,7 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
res.end();
return;
}
-
+
// The headers are okay. Next begin the proxied request.
targetOptions.method = req.method || 'GET';
self.proxiedRequest(pgtIOU, targetOptions, function(err, targetReq, targetRes) {
@@ -829,7 +918,7 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
res.end();
return;
}
-
+
// Mirror requester's data to the target
req.on('data', function(chunk) {
targetReq.write(chunk);
@@ -837,10 +926,10 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
req.on('end', function() {
targetReq.end();
});
-
+
// Mirror target's response headers back to requester
res.writeHead(targetRes.statusCode, targetRes.headers);
-
+
// Mirror target's data back to the requester
targetRes.on('data', function(chunk) {
res.write(chunk);
@@ -857,7 +946,7 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
/**
* Create a CAS proxied HTTP/HTTPS request.
- * The CAS proxy ticket (PT) will automatically be added to the target
+ * The CAS proxy ticket (PT) will automatically be added to the target
* service's query.
*
* Deprecated.
@@ -866,13 +955,13 @@ CAS.prototype.startPgtServer = function(key, cert, ca, callbackHost, callbackPor
* This should have been obtained during the initial CAS login with
* the validate() function.
* @param {Object} options
- * Same as the options passed in to http.request(). This is where you
+ * Same as the options passed in to http.request(). This is where you
* specify the service URL you are requesting.
* @param {Function} callback
* callback(err, req, res)
* @api public
*/
-CAS.prototype.proxiedRequest = function(pgtIOU, options, callback)
+CAS.prototype.proxiedRequest = function(pgtIOU, options, callback)
{
if (this.external_proxy_url) {
this.proxiedRequestExternal(this.external_proxy_url, pgtIOU, options, callback);
@@ -880,13 +969,13 @@ CAS.prototype.proxiedRequest = function(pgtIOU, options, callback)
}
var targetService = url.format(options);
-
+
this.getProxyTicket(pgtIOU, targetService, function(err, pt) {
if (err) {
callback(err);
return;
}
-
+
// Add the proxy ticket to the target service's query string
var path = options.path || targetService.replace(/^https?:\/\/[^\/]+/, '');
if (path.match(/[&?]/)) {
@@ -899,7 +988,7 @@ CAS.prototype.proxiedRequest = function(pgtIOU, options, callback)
delete options.href;
delete options.query;
options.agent = false;
-
+
// Request the target service
var serviceObj;
if (options.options == 'https:') {
@@ -917,7 +1006,7 @@ CAS.prototype.proxiedRequest = function(pgtIOU, options, callback)
callback(undefined, req, res);
});
break;
-
+
case 'POST':
case 'PUT':
// Let the calling function end the request manually after
@@ -933,7 +1022,7 @@ CAS.prototype.proxiedRequest = function(pgtIOU, options, callback)
catch (err) {
callback(err);
}
-
+
});
}
@@ -949,13 +1038,13 @@ CAS.prototype.proxiedRequest = function(pgtIOU, options, callback)
* This should have been obtained during the initial CAS login with
* the validate() function.
* @param {Object} requestOptions
- * Same as the options passed in to http.request(). This is where you
+ * Same as the options passed in to http.request(). This is where you
* specify the service URL you are requesting.
* @param {Function} callback
* callback(err, req, res)
* @api public
*/
-CAS.prototype.proxiedRequestExternal = function(proxyURL, pgtIOU, options, callback)
+CAS.prototype.proxiedRequestExternal = function(proxyURL, pgtIOU, options, callback)
{
var targetService = url.format(options);
var proxyInfo = url.parse(proxyURL);
@@ -975,7 +1064,7 @@ CAS.prototype.proxiedRequestExternal = function(proxyURL, pgtIOU, options, callb
} else {
serviceObj = http;
}
-
+
try {
var req = serviceObj.get(proxyInfo, function(res) {
callback(undefined, req, res);
@@ -1000,13 +1089,13 @@ CAS.prototype.proxiedRequestExternal = function(proxyURL, pgtIOU, options, callb
* }
* @attribution http://downloads.jasig.org/cas-clients/php/1.2.0/docs/api/client_8php_source.html#l01589
*/
-var parseAttributes = function(elemSuccess)
+var parseAttributes = function(elemSuccess)
{
var attributes = {};
var elemAttribute = elemSuccess.find('cas\\:attributes').first();
if (elemAttribute && elemAttribute.children().length > 0) {
// "Jasig Style" Attributes:
- //
+ //
//
//
// jsmith
@@ -1023,7 +1112,7 @@ var parseAttributes = function(elemSuccess)
//
for (var i=0; i
//
// jsmith
- //
+ //
// RubyCAS
// Smith
// John
// CN=Staff,OU=Groups,DC=example,DC=edu
// CN=Spanish Department,OU=Departments,...
- //
+ //
// PGTIOU-84678-8a9d2...
//
//
- //
+ //
for (var i=0; i
//
// jsmith
- //
+ //
//
//
//
//
//
- //
+ //
// PGTIOU-84678-8a9d2sfa23casd
//
//
@@ -1111,6 +1200,6 @@ var parseAttributes = function(elemSuccess)
}
}
}
-
+
return attributes;
}
diff --git a/package.json b/package.json
index e93f1e9..77346e7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cas",
- "version": "0.0.5",
+ "version": "0.0.7",
"description": "Central Authentication Service (CAS) client for Node.js",
"keywords": [
"cas",
@@ -18,7 +18,8 @@
}
],
"dependencies": {
- "cheerio": "0.19.0"
+ "cheerio": "0.22.0",
+ "xml2js": "^0.4.8"
},
"devDependencies": {
"chai": "^3.4.1",