Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implment devMode #12

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 65 additions & 29 deletions lib/passport-cas.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ var url = require('url')
*
* Authentication is done by redirecting the user to the CAS login page. The
* user will return with a ticket in the querystring. This ticket is then
* validated by the application against the CAS server to obtain the username
* validated by the application against the CAS server to obtain the username
* and profile.
*
* (CAS optionally allows the application to obtain tickets for 3rd party
* (CAS optionally allows the application to obtain tickets for 3rd party
* services on behalf of the user. This requires the use of a PGT callback
* server, which can be run with the PgtServer() function also from this
* module.)
Expand Down Expand Up @@ -46,19 +46,20 @@ var url = require('url')
* - `propertyMap` Optional. A basic key-value object for mapping extended user attributes from CAS to passport's profile format.
* - `passReqToCallback` Optional. When `true`, `req` is the first argument to the verify callback (default: `false`)
* - `sslCA` Optional. SSL CA bundle to use to validate the PGT server.
* - `devMode` Optional. Useful when testing locally, assumes a CAS session was successfully created
*
* Example:
*
* var CasStrategy = require('passport-cas2').Strategy;
* var cas = new CasStrategy({
* casURL: 'https://signin.example.com/cas',
* propertyMap: {
* propertyMap: {
* id: 'guid',
* givenName: 'givenname',
* familyName: 'surname',
* emails: 'defaultmail'
* }
* },
* },
* function(username, profile, done) {
* User.findOrCreate(..., function(err, user) {
* done(err, user);
Expand All @@ -77,29 +78,30 @@ function CasStrategy(options, verify) {
options = undefined;
}
options = options || {};

if (!verify) { throw new TypeError('CasStrategy requires a verify callback'); }
if (!options.casURL) { throw new TypeError('CasStrategy requires a casURL option'); }

Strategy.call(this);
this.name = 'cas';
this._verify = verify;
this._passReqToCallback = options.passReqToCallback;

this.casBaseUrl = options.casURL;
this.devMode = options.devMode;
this.casPgtUrl = options.pgtURL || undefined;
this.casPropertyMap = options.propertyMap || {};
this.casSessionKey = options.sessionKey || 'cas';

this.cas = new CAS({
base_url: this.casBaseUrl,
version: 2,
version: 3,
external_pgt_url: this.casPgtUrl,
ssl_cert: options.sslCert,
ssl_key: options.sslKey,
ssl_ca: options.sslCA
});

}

/**
Expand All @@ -117,14 +119,44 @@ util.inherits(CasStrategy, Strategy);
CasStrategy.prototype.authenticate = function(req, options) {
if (!req._passport) { return this.error(new Error('passport.initialize() middleware not in use')); }
options = options || {};

var self = this;
var reqURL = url.parse(req.originalUrl || req.url, true);
var service;


// The provided `verify` callback will call this on completion
function verified(err, user, info) {
if (!self.devMode) {
if (err) { return self.error(err); }
if (!user) { return self.fail(info); }
}
self.success(user, info);
}

if (self.devMode) {
var username = 'test_user';
var profile = {
provider: 'CAS',
id: username,
displayName: username,
name: {
familyName: null,
givenName: null,
middleName: null,
},
emails: [],
};
if (self._passReqToCallback) {
self._verify(req, username, profile, verified);
} else {
self._verify(username, profile, verified);
}
return;
}

// `ticket` is present if user is already authenticated/authorized by CAS
var ticket = reqURL.query['ticket'];

// The `service` string is the current URL, minus the ticket
delete reqURL.query['ticket'];
service = url.format({
Expand All @@ -133,18 +165,22 @@ CasStrategy.prototype.authenticate = function(req, options) {
pathname: req.headers['x-proxied-request-uri'] || reqURL.pathname,
query: reqURL.query
});

if (!ticket) {
// Redirect to CAS server for authentication
self.redirect(self.casBaseUrl + '/login?service=' + encodeURIComponent(service), 307);
var date = new Date(Date.now());
console.log('redirecting');
self.redirect(`${self.casBaseUrl}/login?service=${encodeURIComponent(`${service}/?_cas_retry=${Math.round(date.getTime() / 60000)}`)}`, 307);
}
else {
console.log('else');
// User has returned from CAS site with a ticket
self.cas.validate(ticket, function(err, status, username, extended) {

// Ticket validation failed
if (err) {
var date = new Date();
console.log('not valid');
var date = new Date(Date.now());
var token = Math.round(date.getTime() / 60000);
if (req.query['_cas_retry'] != token) {
// There was a CAS error. A common cause is when an old
Expand All @@ -160,22 +196,23 @@ CasStrategy.prototype.authenticate = function(req, options) {
self.fail(err);
}
}

// Validation successful
else {
console.log('valid');
// The provided `verify` callback will call this on completion
function verified(err, user, info) {
if (err) { return self.error(err); }
if (!user) { return self.fail(info); }
self.success(user, info);
}

req.session[self.casSessionKey] = {};

if (self.casPgtUrl) {
req.session[self.casSessionKey].PGTIOU = extended.PGTIOU;
}

var attributes = extended.attributes;
var profile = {
provider: 'CAS',
Expand All @@ -188,7 +225,7 @@ CasStrategy.prototype.authenticate = function(req, options) {
},
emails: []
};

// Map relevant extended attributes returned by CAS into the profile
for (var key in profile) {
if (key == 'name') {
Expand All @@ -202,7 +239,7 @@ CasStrategy.prototype.authenticate = function(req, options) {
}
delete attributes[mappedKey];
}
}
}
else if (key == 'emails') {
var mappedKey = self.casPropertyMap.emails || 'emails';
var emails = attributes[mappedKey];
Expand All @@ -229,7 +266,7 @@ CasStrategy.prototype.authenticate = function(req, options) {
var value = attributes[mappedKey];
if (Array.isArray(value)) {
profile[key] = value[0];
}
}
else if (value) {
profile[key] = value;
}
Expand All @@ -240,14 +277,14 @@ CasStrategy.prototype.authenticate = function(req, options) {
for (var key in attributes) {
profile[key] = attributes[key];
}

if (self._passReqToCallback) {
self._verify(req, username, profile, verified);
} else {
self._verify(username, profile, verified);
}
}

}, service);
}
};
Expand Down Expand Up @@ -307,7 +344,7 @@ CasStrategy.prototype.getProxyTicket = function(req, targetService, done) {
err = new Error('PGTIOU token not found. Make sure pgtURL option is correct, and the CAS server allows proxies.');
}
}

if (err) {
return done(err);
}
Expand All @@ -333,7 +370,7 @@ module.exports.Strategy = CasStrategy;
* This is the server needed to obtain CAS tickets for 3rd party services on
* behalf of the user. It is typically run as a separate process from the
* application. Multiple applications may share the same PGT callback server.
*
*
* @param {String} casURL
* The URL of the CAS server.
* @param {String} pgtURL
Expand Down Expand Up @@ -367,4 +404,3 @@ function PgtServer(casURL, pgtURL, serverCertificate, serverKey, serverCA) {
* Expose `PgtServer`.
*/
module.exports.PgtServer = PgtServer;