diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index d033749620..9789c90078 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -77,33 +77,12 @@ habitrpg.controller('SettingsCtrl', $rootScope.$state.go('tasks'); } - $scope.changeUsername = function(changeUser){ - if (!changeUser.newUsername || !changeUser.password) { - return alert(window.env.t('fillAll')); - } - $http.post(ApiUrl.get() + '/api/v2/user/change-username', changeUser) + $scope.changeUser = function(attr, updates){ + $http.post(ApiUrl.get() + '/api/v2/user/change-'+attr, updates) .success(function(){ - alert(window.env.t('usernameSuccess')); - $scope.changeUser = {}; + alert(window.env.t(attr+'Success')); + _.each(updates, function(v,k){updates[k]=null;}); User.sync(); - }) - .error(function(data){ - alert(data.err); - }); - } - - $scope.changePassword = function(changePass){ - if (!changePass.oldPassword || !changePass.newPassword || !changePass.confirmNewPassword) { - return alert(window.env.t('fillAll')); - } - $http.post(ApiUrl.get() + '/api/v2/user/change-password', changePass) - .success(function(data, status, headers, config){ - if (data.err) return alert(data.err); - alert(window.env.t('passSuccess')); - $scope.changePass = {}; - }) - .error(function(data, status, headers, config){ - alert(data.err); }); } diff --git a/src/controllers/auth.js b/src/controllers/auth.js index a4c04a458a..911c72b0e8 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -23,6 +23,10 @@ var accountSuspended = function(uuid){ code: 'ACCOUNT_SUSPENDED' }; } +// escape email for regex, then search case-insensitive. See http://stackoverflow.com/a/3561711/362790 +var mongoEmailRegex = function(email){ + return new RegExp('^' + email.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '$', 'i'); +} api.auth = function(req, res, next) { var uid = req.headers['x-api-user']; @@ -196,9 +200,7 @@ api.resetPassword = function(req, res, next){ newPassword = utils.makeSalt(), // use a salt as the new password too (they'll change it later) hashed_password = utils.encryptPassword(newPassword, salt); - // escape email for regex, then search case-insensitive. See http://stackoverflow.com/a/3561711/362790 - var emailRegExp = new RegExp('^' + email.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '$', 'i'); - User.findOne({'auth.local.email':emailRegExp}, function(err, user){ + User.findOne({'auth.local.email':mongoEmailRegex(email)}, function(err, user){ if (err) return next(err); if (!user) return res.send(500, {err:"Couldn't find a user registered for email " + email}); user.auth.local.salt = salt; @@ -215,28 +217,45 @@ api.resetPassword = function(req, res, next){ }); }; +var invalidPassword = function(user, password){ + var hashed_password = utils.encryptPassword(password, user.auth.local.salt); + if (hashed_password !== user.auth.local.hashed_password) + return {code:401, err:"Incorrect password"}; + return false; +} + api.changeUsername = function(req, res, next) { - var user = res.locals.user, - password = req.body.password, - newUsername = req.body.newUsername; + async.waterfall([ + function(cb){ + User.findOne({'auth.local.username': req.body.username}, {auth:1}, cb); + }, + function(found, cb){ + if (found) return cb({code:401, err: "Username already taken"}); + if (invalidPassword(res.locals.user, req.body.password)) return cb(invalidPassword(res.locals.user, req.body.password)); + res.locals.user.auth.local.username = req.body.username; + res.locals.user.save(cb); + } + ], function(err){ + if (err) return err.code ? res.json(err.code, err) : next(err); + res.send(200); + }) +} - User.findOne({'auth.local.username': newUsername}, function(err, result) { - if (err) next(err); - if(result) return res.json(401, {err: "Username already taken"}); - - var salt = user.auth.local.salt; - var hashed_password = utils.encryptPassword(password, salt); - - if (hashed_password !== user.auth.local.hashed_password) - return res.json(401, {err:"Incorrect password"}); - - user.auth.local.username = newUsername; - user.save(function(err, saved){ - if (err) next(err); - res.send(200); - user = password = newUsername = null; - }) - }); +api.changeEmail = function(req, res, next){ + async.waterfall([ + function(cb){ + User.findOne({'auth.local.email': mongoEmailRegex(req.body.email)}, {auth:1}, cb); + }, + function(found, cb){ + if(found) return cb({code:401, err: "Email already taken"}); + if (invalidPassword(res.locals.user, req.body.password)) return cb(invalidPassword(res.locals.user, req.body.password)); + res.locals.user.auth.local.email = req.body.email; + res.locals.user.save(cb); + } + ], function(err){ + if (err) return err.code ? res.json(err.code,err) : next(err); + res.send(200); + }) } api.changePassword = function(req, res, next) { diff --git a/src/routes/auth.js b/src/routes/auth.js index 0405154db1..2080c94697 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -11,6 +11,7 @@ router.post('/api/v2/user/auth/social', i18n.getUserLanguage, auth.loginSocial); 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); +router.post('/api/v2/user/change-email', i18n.getUserLanguage, auth.auth, auth.changeEmail); router.post('/api/v1/register', i18n.getUserLanguage, auth.registerUser); router.post('/api/v1/user/auth/local', i18n.getUserLanguage, auth.loginLocal); diff --git a/views/options/settings.jade b/views/options/settings.jade index 53c756ebcf..2fa6996038 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -103,7 +103,7 @@ script(type='text/ng-template', id='partials/options.settings.settings.html') |  =env.t('loginNameDescription3') p=env.t('email') - |: {{::user.auth.local.email}} + |: {{user.auth.local.email}} p small.muted =env.t('emailChange1') @@ -112,22 +112,33 @@ script(type='text/ng-template', id='partials/options.settings.settings.html') |  =env.t('emailChange3') hr + h5=env.t('changeUsername') - form(ng-submit='changeUsername(changeUser)', ng-show='user.auth.local') + form(ng-submit='changeUser("username", usernameUpdates)', ng-init='usernameUpdates={}', ng-show='user.auth.local', name='changeUsername', novalidate) + //-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted')=env.t('fillAll') .form-group - input.form-control(type='text', placeholder=env.t('newUsername'), ng-model='changeUser.newUsername', required) + input.form-control(type='text', placeholder=env.t('newUsername'), ng-model='usernameUpdates.username', required) .form-group - input.form-control(type='password', placeholder=env.t('password'), ng-model='changeUser.password', required) - input.btn.btn-default(type='submit', value=env.t('submit')) + input.form-control(type='password', placeholder=env.t('password'), ng-model='usernameUpdates.password', required) + input.btn.btn-default(type='submit', ng-disabled='changeUsername.$invalid', value=env.t('submit')) + + h5=env.t('changeEmail') + form(ng-submit='changeUser("email", emailUpdates)', ng-show='user.auth.local', name='changeEmail', novalidate) + .form-group + input.form-control(type='text', placeholder=env.t('newEmail'), ng-model='emailUpdates.email', required) + .form-group + input.form-control(type='password', placeholder=env.t('password'), ng-model='emailUpdates.password', required) + input.btn.btn-default(type='submit', ng-disabled='changeEmail.$invalid', value=env.t('submit')) + h5=env.t('changePass') - form(ng-submit='changePassword(changePass)', ng-show='user.auth.local') + form(ng-submit='changeUser("password", passwordUpdates)', ng-show='user.auth.local', name='changePassword', novalidate) .form-group - input.form-control(type='password', placeholder=env.t('oldPass'), ng-model='changePass.oldPassword', required) + input.form-control(type='password', placeholder=env.t('oldPass'), ng-model='passwordUpdates.oldPassword', required) .form-group - input.form-control(type='password', placeholder=env.t('newPass'), ng-model='changePass.newPassword', required) + input.form-control(type='password', placeholder=env.t('newPass'), ng-model='passwordUpdates.newPassword', required) .form-group - input.form-control(type='password', placeholder=env.t('confirmPass'), ng-model='changePass.confirmNewPassword', required) - input.btn.btn-default(type='submit', value=env.t('submit')) + input.form-control(type='password', placeholder=env.t('confirmPass'), ng-model='passwordUpdates.confirmNewPassword', required) + input.btn.btn-default(type='submit', ng-disabled='changePassword.$invalid', value=env.t('submit')) .panel.panel-default