diff --git a/packages/accounts-base/accounts_server.js b/packages/accounts-base/accounts_server.js index 8b4dc17346..50b8bd0d81 100644 --- a/packages/accounts-base/accounts_server.js +++ b/packages/accounts-base/accounts_server.js @@ -103,6 +103,10 @@ return _.extend(user, extra); }; Meteor.accounts.onCreateUserHook = function (options, extra, user) { + // add created at timestamp (and protect passed in user object from + // modification) + user = _.extend({createdAt: +(new Date)}, user); + var fullUser; if (onCreateUserHook) { @@ -118,13 +122,22 @@ } _.each(validateNewUserHooks, function (hook) { - if (!hook(user)) + if (!hook(fullUser)) throw new Meteor.Error(403, "User validation failed"); }); - // XXX check for existing user with duplicate email or username. - // better here than the two places we call it (and immediately - // follow with an insert) + // check for existing user with duplicate email or username. + if (fullUser.username && + Meteor.users.findOne({username: fullUser.username})) + throw new Meteor.Error(403, "Username already exists."); + + if (fullUser.emails) { + var addresses = _.map(fullUser.emails, function (e) { + return e.address; }); + if (Meteor.users.findOne({'emails.address': {$in: addresses}})) + throw new Meteor.Error(403, "Email already exists."); + } + return fullUser; }; diff --git a/packages/accounts-base/accounts_tests.js b/packages/accounts-base/accounts_tests.js index 13f939fe06..4c4d20f015 100644 --- a/packages/accounts-base/accounts_tests.js +++ b/packages/accounts-base/accounts_tests.js @@ -39,3 +39,94 @@ Tinytest.add('accounts - updateOrCreateUser', function (test) { }); +Tinytest.add('accounts - onCreateUserHook username', function (test) { + var userIn = { + username: Meteor.uuid() + }; + + // user does not already exist. return a user object with fields set. + var userOut = Meteor.accounts.onCreateUserHook( + userIn, + {profile: {name: 'Foo Bar'}}, + userIn + ); + + test.equal(typeof userOut.createdAt, 'number'); + test.equal(userOut.profile.name, 'Foo Bar'); + test.equal(userOut.username, userIn.username); + + // insert the user + var uid = Meteor.users.insert(userOut); + + // run the hook again. now the user exists, so it throws an error. + test.throws(function () { + Meteor.accounts.onCreateUserHook( + userIn, + {profile: {name: 'Foo Bar'}}, + userIn + ); + }); + + // cleanup + Meteor.users.remove(uid); + +}); + +Tinytest.add('accounts - onCreateUserHook email', function (test) { + var email1 = Meteor.uuid(); + var email2 = Meteor.uuid(); + var email3 = Meteor.uuid(); + var userIn = { + emails: [{address: email1, verified: false}, + {address: email2, verified: true}] + }; + + // user does not already exist. return a user object with fields set. + var userOut = Meteor.accounts.onCreateUserHook( + userIn, + {profile: {name: 'Foo Bar'}}, + userIn + ); + + test.equal(typeof userOut.createdAt, 'number'); + test.equal(userOut.profile.name, 'Foo Bar'); + test.equal(userOut.emails, userIn.emails); + + // insert the user + var uid = Meteor.users.insert(userOut); + + // run the hook again with the exact same emails. + // run the hook again. now the user exists, so it throws an error. + test.throws(function () { + Meteor.accounts.onCreateUserHook( + userIn, + {profile: {name: 'Foo Bar'}}, + userIn + ); + }); + + // now with only one of them. + test.throws(function () { + Meteor.accounts.onCreateUserHook( + {}, {}, {emails: [{address: email1}]} + ); + }); + + test.throws(function () { + Meteor.accounts.onCreateUserHook( + {}, {}, {emails: [{address: email2}]} + ); + }); + + + // a third email works. + var user3 = Meteor.accounts.onCreateUserHook( + {}, {}, {emails: [{address: email3}]} + ); + test.equal(typeof userOut.createdAt, 'number'); + + + // cleanup + Meteor.users.remove(uid); + +});