-
Notifications
You must be signed in to change notification settings - Fork 355
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial pass at adding middleware for users
- Loading branch information
Showing
14 changed files
with
352 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/** | ||
* authorization contains the references to the authorization middleware. | ||
* @type {Object} | ||
*/ | ||
const authorization = module.exports = {}; | ||
|
||
const debug = require('debug')('talk:middleware:authorization'); | ||
|
||
/** | ||
* ErrNotAuthorized is an error that is returned in the event an operation is | ||
* deemed not authorized. | ||
* @type {Error} | ||
*/ | ||
const ErrNotAuthorized = new Error('not authorized'); | ||
ErrNotAuthorized.status = 401; | ||
|
||
// Add the ErrNotAuthorized error to the authorization object to be exported. | ||
authorization.ErrNotAuthorized = ErrNotAuthorized; | ||
|
||
/** | ||
* has returns true if the user has all the roles specified, otherwise it will | ||
* return false. | ||
* @param {Object} user the user to check for roles | ||
* @param {Array} roles all the roles that a user must have | ||
* @return {Boolean} true if the user has all the roles required, false | ||
* otherwise | ||
*/ | ||
authorization.has = (user, ...roles) => roles.every((role) => user.roles.indexOf(role) >= 0); | ||
|
||
/** | ||
* needed is a connect middleware layer that ensures that all requests coming | ||
* here are both authenticated and match a set of roles required to continue. | ||
* @param {Array} roles all the roles that a user must have | ||
* @return {Callback} connect middleware | ||
*/ | ||
authorization.needed = (...roles) => (req, res, next) => { | ||
// All routes that are wrapepd with this middleware actually require a role. | ||
if (!req.user) { | ||
debug(`No user on request, returning with ${ErrNotAuthorized}`); | ||
return next(ErrNotAuthorized); | ||
} | ||
|
||
// Check to see if the current user has all the roles requested for the given | ||
// array of roles requested, if one is not on the user, then this will | ||
// evaluate to true. | ||
if (!authorization.has(req.user, ...roles)) { | ||
debug('User does not have all the required roles to access this page'); | ||
return next(ErrNotAuthorized); | ||
} | ||
|
||
// Looks like they're allowed! | ||
return next(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
const passport = require('passport'); | ||
const User = require('./models/user'); | ||
const LocalStrategy = require('passport-local').Strategy; | ||
const FacebookStrategy = require('passport-facebook').Strategy; | ||
|
||
//============================================================================== | ||
// SESSION SERIALIZATION | ||
//============================================================================== | ||
|
||
passport.serializeUser((user, done) => { | ||
done(null, user.id); | ||
}); | ||
|
||
passport.deserializeUser((id, done) => { | ||
User | ||
.findById(id) | ||
.then((user) => { | ||
done(null, user); | ||
}) | ||
.catch((err) => { | ||
done(err); | ||
}); | ||
}); | ||
|
||
/** | ||
* Validates that a user is allowed to login. | ||
* @param {User} user the user to be validated | ||
* @param {Function} done the callback for the validation | ||
*/ | ||
function ValidateUserLogin(user, done) { | ||
if (!user) { | ||
return done(new Error('user not found')); | ||
} | ||
|
||
if (user.disabled) { | ||
return done(null, false, {message: 'Account disabled'}); | ||
} | ||
|
||
return done(null, user); | ||
} | ||
|
||
//============================================================================== | ||
// STRATEGIES | ||
//============================================================================== | ||
|
||
passport.use(new LocalStrategy({ | ||
usernameField: 'email', | ||
passwordField: 'password' | ||
}, (email, password, done) => { | ||
User | ||
.findLocalUser(email, password) | ||
.then((user) => { | ||
if (!user) { | ||
return done(null, false, {message: 'Incorrect email/password combination'}); | ||
} | ||
|
||
return ValidateUserLogin(user, done); | ||
}) | ||
.catch((err) => { | ||
done(err); | ||
}); | ||
})); | ||
|
||
if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET && process.env.TALK_ROOT_URL) { | ||
passport.use(new FacebookStrategy({ | ||
clientID: process.env.TALK_FACEBOOK_APP_ID, | ||
clientSecret: process.env.TALK_FACEBOOK_APP_SECRET, | ||
callbackURL: `${process.env.TALK_ROOT_URL}/connect/facebook/callback` | ||
}, (accessToken, refreshToken, profile, done) => { | ||
User | ||
.findOrCreateExternalUser(profile) | ||
.then((user) => | ||
ValidateUserLogin(user, done) | ||
) | ||
.catch((err) => { | ||
done(err); | ||
}); | ||
})); | ||
} else { | ||
console.error('Facebook cannot be enabled, missing one of TALK_FACEBOOK_APP_ID, TALK_FACEBOOK_APP_SECRET, TALK_ROOT_URL'); | ||
} | ||
|
||
module.exports = passport; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
const redis = require('redis'); | ||
const debug = require('debug')('talk:redis'); | ||
const url = process.env.TALK_REDIS_URL || 'redis://localhost'; | ||
|
||
const client = redis.createClient(url, { | ||
retry_strategy: function(options) { | ||
if (options.error.code === 'ECONNREFUSED') { | ||
|
||
// End reconnecting on a specific error and flush all commands with a individual error | ||
return new Error('The server refused the connection'); | ||
} | ||
if (options.total_retry_time > 1000 * 60 * 60) { | ||
|
||
// End reconnecting after a specific timeout and flush all commands with a individual error | ||
return new Error('Retry time exhausted'); | ||
} | ||
|
||
if (options.times_connected > 10) { | ||
|
||
// End reconnecting with built in error | ||
return undefined; | ||
} | ||
|
||
// reconnect after | ||
return Math.max(options.attempt * 100, 3000); | ||
} | ||
}); | ||
|
||
// client.on('error', (err) => { | ||
// throw err; | ||
// }); | ||
|
||
client.ping((err) => { | ||
if (err) { | ||
console.error('Can\'t ping the redis server!'); | ||
|
||
throw err; | ||
} | ||
|
||
debug('connection established'); | ||
}); | ||
|
||
module.exports = client; |
Oops, something went wrong.