Add createUser on the server.

Not very useful yet, since it doesn't send welcome emails.
This commit is contained in:
Nick Martin
2012-08-14 22:54:32 -07:00
parent 1113b3a21e
commit 9eff2a109b
5 changed files with 159 additions and 66 deletions

View File

@@ -14,7 +14,7 @@ Package.on_use(function(api) {
Package.on_test(function(api) {
api.use(['accounts-passwords', 'tinytest', 'test-helpers']);
api.add_files('passwords_tests_setup.js', 'server');
api.add_files('passwords_tests.js', 'client');
api.add_files('passwords_tests.js', ['client', 'server']);
api.add_files('email_tests_setup.js', 'server');
api.add_files('email_tests.js', 'client');
});

View File

@@ -1,5 +1,7 @@
(function () {
Meteor.createUser = function (options, extra, callback) {
options = _.clone(options); // we'll be modifying options
if (typeof extra === "function") {
callback = extra;
extra = {};
@@ -8,15 +10,14 @@
if (!options.password)
throw new Error("Must set options.password");
var verifier = Meteor._srp.generateVerifier(options.password);
// strip old password, replacing with the verifier object
delete options.password;
options.srp = verifier;
if (options.validation)
// needed because we generate a link back to the app
options.baseUrl = appBaseUrl();
// strip old password, replacing with the verifier object
delete options.password;
options.srp = verifier;
Meteor.apply('createUser', [options, extra], {wait: true},
function (error, result) {
if (error || !result) {

View File

@@ -104,49 +104,6 @@
return ret;
},
createUser: function (options, extra) {
extra = extra || {};
var username = options.username;
var email = options.email;
if (!username && !email)
throw new Meteor.Error(400, "Need to set a username or email");
if (options.validation && !options.baseUrl)
throw new Meteor.Error(
400, "If options.validation is set, need to pass options.baseUrl");
if (username && Meteor.users.findOne({username: username}))
throw new Meteor.Error(403, "User already exists with username " + username);
if (email && Meteor.users.findOne({"emails.email": email})) {
throw new Meteor.Error(403, "User already exists with email " + email);
}
// XXX validate verifier
// raw password, should only be used over SSL!
if (options.password) {
if (options.srp)
throw new Meteor.Error(400, "Don't pass both password and srp in options");
options.srp = Meteor._srp.generateVerifier(options.password);
}
var user = {services: {password: {srp: options.srp}}};
if (username)
user.username = username;
if (email)
user.emails = [{email: email, validated: false}];
user = Meteor.accounts.onCreateUserHook(options, extra, user);
var userId = Meteor.users.insert(user);
// If `options.validation` is set, register a token to validate
// the user's primary email, and send it to that address.
if (email && options.validation)
Meteor.accounts.sendValidationEmail(userId, email, options.baseUrl);
var loginToken = Meteor.accounts._loginTokens.insert({userId: userId});
this.setUserId(userId);
return {token: loginToken, id: userId};
},
forgotPassword: function (options) {
var email = options.email;
var baseUrl = options.baseUrl;
@@ -290,6 +247,125 @@
return {token: loginToken, id: user._id};
});
////////////
// Creating users:
// Shared createUser function called from the createUser method, both
// if originates in client or server code. Calls user provided hooks,
// does the actual user insertion.
//
// returns userId or throws an error if it can't create
var createUser = function (options, extra) {
extra = extra || {};
var username = options.username;
var email = options.email;
if (!username && !email)
throw new Meteor.Error(400, "Need to set a username or email");
// XXX need to get base url on the server somehow, for welcome
// emails at least.
if (options.validation && !options.baseUrl)
throw new Meteor.Error(
400, "If options.validation is set, need to pass options.baseUrl");
if (username && Meteor.users.findOne({username: username}))
throw new Meteor.Error(403, "User already exists with username " + username);
if (email && Meteor.users.findOne({"emails.email": email})) {
throw new Meteor.Error(403, "User already exists with email " + email);
}
// Raw password. The meteor client doesn't send this, but a DDP
// client that didn't implement SRP could send this. This should
// only be done over SSL.
if (options.password) {
if (options.srp)
throw new Meteor.Error(400, "Don't pass both password and srp in options");
options.srp = Meteor._srp.generateVerifier(options.password);
}
var user = {services: {}};
if (options.srp)
user.services.password = {srp: options.srp}; // XXX validate verifier
if (username)
user.username = username;
if (email)
user.emails = [{email: email, validated: false}];
user = Meteor.accounts.onCreateUserHook(options, extra, user);
var userId = Meteor.users.insert(user);
// If `options.validation` is set, register a token to validate
// the user's primary email, and send it to that address.
if (email && options.validation)
Meteor.accounts.sendValidationEmail(userId, email, options.baseUrl);
// XXX send welcome email.
//
// Is a welcome email just a validation email with a password prompt
// as well?
return userId;
};
// method for create user. Requests come from the client.
Meteor.methods({
createUser: function (options, extra) {
if (Meteor.accounts._options.forbidSignups)
throw new Meteor.Error(403, "Signups forbidden");
var userId = createUser(options, extra);
// safety belt. createUser is supposed to throw on error. send 500
// error instead of creating a login token with empty userid.
if (!userId)
throw new Error("createUser failed to insert new user");
// client gets logged in as the new user afterwards.
var loginToken = Meteor.accounts._loginTokens.insert({userId: userId});
this.setUserId(userId);
return {token: loginToken, id: userId};
}
});
// Create user directly on the server.
//
// Unlike the client version, this does not log you in as this user
// after creation.
Meteor.createUser = function (options, extra, callback) {
if (typeof extra === "function") {
callback = extra;
extra = {};
}
// XXX relax these constraints!
if (callback) {
throw new Error("Meteor.createUser with callback not supported on the server yet.");
}
if (options.password || options.srp)
throw new Error("Meteor.createUser on the server does not let you set a password yet.");
if (!options.email)
throw new Error("Meteor.createUser on the server requires email.");
// XXX we don't have a base url, so we don't know how to generate links.
if (options.validation)
throw new Error("Validation email from server not supported yet.");
var userId = createUser(options, extra);
return userId;
};
})();

View File

@@ -1,4 +1,4 @@
(function () {
if (Meteor.is_client) (function () {
// XXX note, only one test can do login/logout things at once! for
// now, that is this test.
@@ -167,15 +167,41 @@
test.equal(Meteor.user().touchedByOnCreateUser, true);
}));
},
// can't call onCreateUserHook twice
function(test, expect) {
Meteor.call('setupMoreThanOneOnCreateUserHook',
{testOnCreateUserHook: true}, expect(function (error) {
test.equal(error.error, 999);
}));
},
logoutStep
// XXX test Meteor.accounts.config(unsafePasswordChanges)
]);
}) ();
if (Meteor.is_server) (function () {
Tinytest.add(
'passwords - setup more than one onCreateUserHook',
function (test) {
test.throws(function() {
Meteor.accounts.onCreateUser(function () {});
});
});
Tinytest.add(
'passwords - createUser hooks',
function (test) {
var email = Meteor.uuid() + '@example.com';
test.throws(function () {
Meteor.createUser({email: email},
{invalid: true}); // should fail the new user validators
});
var userId = Meteor.createUser({email: email},
{testOnCreateUserHook: true});
test.isTrue(userId);
var user = Meteor.users.findOne(userId);
test.equal(user.touchedByOnCreateUser, true);
});
// XXX would be nice to test Meteor.accounts.config({forbidSignups: true})
}) ();

View File

@@ -11,16 +11,6 @@ Meteor.accounts.onCreateUser(function (options, extra, user) {
}
});
Meteor.methods({
setupMoreThanOneOnCreateUserHook: function () {
try {
Meteor.accounts.onCreateUser(function () {});
} catch (exception) {
throw new Meteor.Error(999, "Test exception");
}
}
});
// Because this is global state that affects every client, we can't turn
// it on and off during the tests. Doing so would mean two simultaneous