diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index 4560c58383..efe2862967 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -242,5 +242,14 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ window.location.href = url; window.location.reload(false); } + + // Universal method for sending HTTP methods + $rootScope.http = function(method, route, data, alertMsg){ + $http[method](ApiUrl.get() + route, data).success(function(){ + if (alertMsg) Notification.text(window.env.t(alertMsg)); + User.sync(); + }); + // error will be handled via $http interceptor + } } ]); diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 911c72b0e8..b3fe5e4d3d 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -65,55 +65,57 @@ api.authWithUrl = function(req, res, next) { } api.registerUser = function(req, res, next) { - var confirmPassword = req.body.confirmPassword, - email = req.body.email, - password = req.body.password, - username = req.body.username; - if (!(username && password && email)) return res.json(401, {err: ":username, :email, :password, :confirmPassword required"}); - if (password !== confirmPassword) return res.json(401, {err: ":password and :confirmPassword don't match"}); - if (!validator.isEmail(email)) return res.json(401, {err: ":email invalid"}); - async.waterfall([ - function(cb) { - User.findOne({'auth.local.email': email}, cb); + async.auto({ + validate: function(cb) { + if (!(req.body.username && req.body.password && req.body.email)) + return cb({code:401, err: ":username, :email, :password, :confirmPassword required"}); + if (req.body.password !== req.body.confirmPassword) + return cb({code:401, err: ":password and :confirmPassword don't match"}); + if (!validator.isEmail(req.body.email)) + return cb({code:401, err: ":email invalid"}); + cb(); }, - function(found, cb) { - if (found) return cb("Email already taken"); - User.findOne({'auth.local.username': username}, cb); - }, function(found, cb) { - var newUser, salt, user; - if (found) return cb("Username already taken"); - salt = utils.makeSalt(); - newUser = { + findEmail: function(cb) { + User.findOne({'auth.local.email': req.body.email}, cb); + }, + findUname: function(cb) { + User.findOne({'auth.local.username': req.body.username}, cb); + }, + findFacebook: function(cb){ + User.findOne({_id: req.headers['x-api-user'], apiToken: req.headers['x-api-key']}, {auth:1}, cb); + }, + register: ['validate', 'findEmail', 'findUname', 'findFacebook', function(cb, data) { + if (data.findEmail) return cb({code:401, err:"Email already taken"}); + if (data.findUname) return cb({code:401, err:"Username already taken"}); + var salt = utils.makeSalt(); + var newUser = { auth: { local: { - username: username, - email: email, + username: req.body.username, + email: req.body.email, salt: salt, - hashed_password: utils.encryptPassword(password, salt) + hashed_password: utils.encryptPassword(req.body.password, salt) }, timestamps: {created: +new Date(), loggedIn: +new Date()} } }; - newUser.preferences = newUser.preferences || {}; - newUser.preferences.language = req.language; // User language detected from browser, not saved - user = new User(newUser); - - // temporary for conventions - if (req.subdomains[0] == 'con') { - _.each(user.dailys, function(h){ - h.repeat = {m:false,t:false,w:false,th:false,f:false,s:false,su:false}; - }) - user.extra = {signupEvent: 'wondercon'}; + // existing user, allow them to add local authentication + if (data.findFacebook) { + data.findFacebook.auth.local = newUser.auth.local; + data.findFacebook.save(cb); + // new user, register them + } else { + newUser.preferences = newUser.preferences || {}; + newUser.preferences.language = req.language; // User language detected from browser, not saved + var user = new User(newUser); + utils.txnEmail(user, 'welcome'); + ga.event('register', 'Local').send(); + user.save(cb); } - - user.save(cb); - utils.txnEmail(user, 'welcome'); - ga.event('register', 'Local').send() - } - ], function(err, saved) { - if (err) return res.json(401, {err: err}); - res.json(200, saved); - email = password = username = null; + }] + }, function(err, data) { + if (err) return err.code ? res.json(err.code, err) : next(err); + res.json(200, data.register[0]); }); }; @@ -190,9 +192,18 @@ api.loginSocial = function(req, res, next) { /** * DELETE /user/auth/social - * TODO implement */ -api.deleteSocial = function(req,res,next){next()} +api.deleteSocial = function(req,res,next){ + if (!res.locals.user.auth.local.username) + return res.json(401, {err:"Account lacks another authentication method, can't detach Facebook"}); + //FIXME for some reason, the following gives https://gist.github.com/lefnire/f93eb306069b9089d123 + //res.locals.user.auth.facebook = null; + //res.locals.user.auth.save(function(err, saved){ + User.update({_id:res.locals.user._id}, {$unset:{'auth.facebook':1}}, function(err){ + if (err) return next(err); + res.send(200); + }) +} api.resetPassword = function(req, res, next){ var email = req.body.email, diff --git a/src/routes/auth.js b/src/routes/auth.js index 2080c94697..442c919f6b 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -8,6 +8,7 @@ auth.setupPassport(router); //FIXME make this consistent with the others router.post('/api/v2/register', i18n.getUserLanguage, auth.registerUser); router.post('/api/v2/user/auth/local', i18n.getUserLanguage, auth.loginLocal); router.post('/api/v2/user/auth/social', i18n.getUserLanguage, auth.loginSocial); +router.delete('/api/v2/user/auth/social', i18n.getUserLanguage, auth.auth, auth.deleteSocial); router.post('/api/v2/user/reset-password', i18n.getUserLanguage, auth.resetPassword); router.post('/api/v2/user/change-password', i18n.getUserLanguage, auth.auth, auth.changePassword); router.post('/api/v2/user/change-username', i18n.getUserLanguage, auth.auth, auth.changeUsername); diff --git a/views/options/settings.jade b/views/options/settings.jade index 7822c252a0..a5cee15b45 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -91,7 +91,24 @@ script(type='text/ng-template', id='partials/options.settings.settings.html') .panel-heading span Registration .panel-body - p(ng-if='user.auth.facebook.id')=env.t('registeredWithFb') + div(ng-if='user.auth.facebook.id') + button.btn.btn-primary(disabled='disabled', ng-if='!user.auth.local.username')=env.t('registeredWithFb') + button.btn.btn-danger(ng-click='http("delete","/api/v2/user/auth/social",null,"detachedFacebook")', ng-if='user.auth.local.username')=env.t('detachFacebook') + hr + div(ng-if='!user.auth.local.username') + p Add local authentication: + form(ng-submit='http("post","/api/v2/register",localAuth,"addedLocalAuth")', ng-init='localAuth={}', name='localAuth', novalidate) + //-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted')=env.t('fillAll') + .form-group + input.form-control(type='text', placeholder=env.t('username'), ng-model='localAuth.username', required) + .form-group + input.form-control(type='text', placeholder=env.t('email'), ng-model='localAuth.email', required) + .form-group + input.form-control(type='password', placeholder=env.t('password'), ng-model='localAuth.password', required) + .form-group + input.form-control(type='password', placeholder=env.t('confirmPass'), ng-model='localAuth.confirmPassword', required) + input.btn.btn-default(type='submit', ng-disabled='localAuth.$invalid', value=env.t('submit')) + div(ng-if='user.auth.local.username') p=env.t('username') |: {{user.auth.local.username}}