diff --git a/common/models/user.json b/common/models/user.json index 6733437349..84ee2a1ba6 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -29,7 +29,6 @@ }, "username": { "type": "string", - "sparse": true, "lowercase": true, "trim": true }, @@ -175,6 +174,12 @@ }, "validations": [], "relations": {}, - "acls": [], + "acls": [{ + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW", + "property": "doesExist" + }], "methods": [] } diff --git a/gulpfile.js b/gulpfile.js index 1a3177de62..4cb0341a96 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -29,7 +29,7 @@ gulp.task('serve', function(cb) { ignore: paths.serverIgnore, env: { 'NODE_ENV': 'development', - 'DEBUG': 'freecc:*' + 'DEBUG': process.env.DEBUG || 'freecc:*' } }) .on('start', function() { diff --git a/package.json b/package.json index d9fd32ff7e..acbc84c80d 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "lodash": "^3.9.3", "loopback": "^2.18.0", "loopback-boot": "^2.8.0", - "loopback-component-passport": "^1.3.1", + "loopback-component-passport": "git://github.com/FreeCodeCamp/loopback-component-passport.git#feature/emailOptional", "loopback-connector-mongodb": "^1.10.0", "lusca": "~1.0.2", "method-override": "~2.3.0", diff --git a/public/js/main_0.0.2.js b/public/js/main_0.0.2.js index c8e67d6aaf..65ec9f170c 100644 --- a/public/js/main_0.0.2.js +++ b/public/js/main_0.0.2.js @@ -427,10 +427,10 @@ profileValidation.directive('uniqueUsername', ['$http', function($http) { var config = { params: { username: username } }; $http .get('/api/users/exists', config) - .success(function (exists) { + .success(function (result) { if (username === scope.storedUsername) { ngModel.$setValidity('unique', true); - } else if (exists) { + } else if (result.exists) { ngModel.$setValidity('unique', false); } }); @@ -458,8 +458,8 @@ profileValidation.directive('existingUsername', var config = { params: { username: username } }; $http .get('/api/users/exists', config) - .success(function(exists) { - ngModel.$setValidity('exists', exists); + .success(function(result) { + ngModel.$setValidity('exists', result.exists); }); } }); @@ -475,14 +475,14 @@ profileValidation.directive('uniqueEmail', ['$http', function($http) { element.bind("keyup", function (event) { ngModel.$setValidity('unique', true); var email = element.val(); - if (element.val()) { + if (email) { var config = { params: { email: email } }; $http .get('/api/users/exists', config) - .success(function (exists) { + .success(function(result) { if (email === scope.storedEmail) { ngModel.$setValidity('unique', true); - } else if (exists) { + } else if (result.exists) { ngModel.$setValidity('unique', false); } }); diff --git a/server/boot/a-extendUser.js b/server/boot/a-extendUser.js index a719949eda..ebb7b4724a 100644 --- a/server/boot/a-extendUser.js +++ b/server/boot/a-extendUser.js @@ -2,6 +2,11 @@ var debug = require('debug')('freecc:extendUser'); module.exports = function(app) { var User = app.models.User; + // NOTE(berks): user email validation currently not needed but build in. This + // work around should let us sneak by + // see: + // https://github.com/strongloop/loopback/issues/1137#issuecomment-109200135 + delete User.validations.email; debug('setting up user hooks'); // send verification email to new camper User.afterRemote('create', function(ctx, user, next) { @@ -59,17 +64,26 @@ module.exports = function(app) { }); User.doesExist = function doesExist(username, email, cb) { + if (!username && !email) { + return process.nextTick(function() { + cb(null, false); + }); + } debug('checking existence'); var where = {}; if (username) { - where.username = username; + where.username = username.toLowerCase(); } else { - where.email = email; + where.email = email ? email.toLowerCase() : email; } + debug('where', where); User.count( - { where: where }, + where, function (err, count) { - if (err) { return cb(err); } + if (err) { + debug('err checking existance: ', err); + return cb(err); + } if (count > 0) { return cb(null, true); } @@ -99,7 +113,8 @@ module.exports = function(app) { } ], http: { - path: '/exists' + path: '/exists', + verb: 'get' } } ); diff --git a/server/boot/user.js b/server/boot/user.js index 1f7c74a7c5..e2e4ec0494 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -9,12 +9,6 @@ var _ = require('lodash'), secrets = require('../../config/secrets'); module.exports = function(app) { - // NOTE(berks): user email validation currently not needed but build in. This - // work around should let us sneak by - // see: - // https://github.com/strongloop/loopback/issues/1137#issuecomment-109200135 - delete app.models.User.validations.email; - var router = app.loopback.Router(); var User = app.models.User; var Story = app.models.Story; @@ -27,7 +21,6 @@ module.exports = function(app) { res.redirect(301, '/signout'); }); router.get('/signin', getSignin); - // router.post('/signin', postSignin); router.get('/signout', signout); router.get('/forgot', getForgot); router.post('/forgot', postForgot); @@ -35,7 +28,6 @@ module.exports = function(app) { router.post('/reset/:token', postReset); router.get('/email-signup', getEmailSignup); router.get('/email-signin', getEmailSignin); - // router.post('/email-signup', postEmailSignup); router.get('/account/api', getAccountAngular); router.post('/account/profile', postUpdateProfile); router.post('/account/password', postUpdatePassword); @@ -127,15 +119,14 @@ module.exports = function(app) { */ function returnUser (req, res, next) { - User.find( - {where: { 'username': req.params.username.toLowerCase() }}, + User.findOne( + { where: { username: req.params.username.toLowerCase() } }, function(err, user) { if (err) { debug('Username err: ', err); return next(err); } - if (user[0]) { - user = user[0]; + if (user) { user.progressTimestamps = user.progressTimestamps.sort(function(a, b) { return a - b; @@ -270,7 +261,9 @@ module.exports = function(app) { return res.redirect('/account'); } - User.findOne({ email: req.body.email }, function(err, existingEmail) { + User.findOne({ + where: { email: req.body.email } + }, function(err, existingEmail) { if (err) { return next(err); } @@ -282,7 +275,7 @@ module.exports = function(app) { return res.redirect('/account'); } User.findOne( - { 'profile.username': req.body.username }, + { where: { username: req.body.username } }, function(err, existingUsername) { if (err) { return next(err); @@ -290,7 +283,7 @@ module.exports = function(app) { var user = req.user; if ( existingUsername && - existingUsername.profile.username !== user.username + existingUsername.username !== user.username ) { req.flash('errors', { msg: 'An account with that username already exists.' @@ -567,7 +560,7 @@ module.exports = function(app) { }, function(token, done) { User.findOne({ - email: req.body.email.toLowerCase() + where: { email: req.body.email.toLowerCase() } }, function(err, user) { if (err) { return done(err); } if (!user) { diff --git a/server/server.js b/server/server.js index 714e5940cc..cce2ead0ac 100755 --- a/server/server.js +++ b/server/server.js @@ -10,6 +10,7 @@ process.on('uncaughtException', function (err) { }); var R = require('ramda'), + assign = require('lodash').assign, loopback = require('loopback'), boot = require('loopback-boot'), accepts = require('accepts'), @@ -202,6 +203,7 @@ passportConfigurator.setupModels({ }); var passportOptions = { + emailOptional: true, profileToUser: function(provider, profile) { var emails = profile.emails; // NOTE(berks): get email or set to null. @@ -210,7 +212,8 @@ var passportOptions = { emails[0].value : null; - var username = provider + '.' + (profile.username || profile.id); + var username = (profile.username || profile.id); + username = typeof username === 'string' ? username.toLowerCase() : username; var password = generateKey('password'); var userObj = { username: username, @@ -227,7 +230,10 @@ var passportOptions = { R.keys(passportProviders).map(function(strategy) { var config = passportProviders[strategy]; config.session = config.session !== false; - passportConfigurator.configureProvider(strategy, config, passportOptions); + passportConfigurator.configureProvider( + strategy, + assign(config, passportOptions) + ); }); /**