mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Implement a reusable AccountsCommon constructor.
This involves moving Accounts.* methods defined in accounts_common.js onto AccountsCommon.prototype.*.
This commit is contained in:
@@ -1,12 +1,64 @@
|
||||
/**
|
||||
* @namespace Accounts
|
||||
* @summary The namespace for all accounts-related methods.
|
||||
*/
|
||||
Accounts = {};
|
||||
// @summary Super-constructor for AccountsClient an AccountsServer.
|
||||
// @locus Anywhere
|
||||
// @param options {Object} an object with fields:
|
||||
// - connection {Object} Optional DDP connection to reuse.
|
||||
// - ddpUrl {String} Optional URL for creating a new DDP connection.
|
||||
AccountsCommon = function AccountsCommon(options) {
|
||||
// Currently this is read directly by packages like accounts-password
|
||||
// and accounts-ui-unstyled.
|
||||
this._options = {};
|
||||
|
||||
// Currently this is read directly by packages like accounts-password
|
||||
// and accounts-ui-unstyled.
|
||||
Accounts._options = {};
|
||||
// Note that setting this.connection = null causes this.users to be a
|
||||
// LocalCollection, which is not what we want.
|
||||
this.connection = undefined;
|
||||
this._initConnection(options || {});
|
||||
|
||||
// There is an allow call in accounts_server.js that restricts writes to
|
||||
// this collection.
|
||||
this.users = new Mongo.Collection("users", {
|
||||
_preventAutopublish: true,
|
||||
connection: this.connection
|
||||
});
|
||||
|
||||
// Callback exceptions are printed with Meteor._debug and ignored.
|
||||
this._onLoginHook = new Hook({
|
||||
debugPrintExceptions: "onLogin callback"
|
||||
});
|
||||
|
||||
this._onLoginFailureHook = new Hook({
|
||||
debugPrintExceptions: "onLoginFailure callback"
|
||||
});
|
||||
};
|
||||
|
||||
var Ap = AccountsCommon.prototype;
|
||||
|
||||
/**
|
||||
* @summary Get the current user id, or `null` if no user is logged in. A reactive data source.
|
||||
* @locus Anywhere but publish functions
|
||||
*/
|
||||
Ap.userId = function () {
|
||||
throw new Error("userId method not implemented");
|
||||
};
|
||||
|
||||
Ap.user = function () {
|
||||
var userId = this.userId();
|
||||
return userId ? this.users.findOne(userId) : null;
|
||||
};
|
||||
|
||||
// Note that Accounts is defined separately in accounts_client.js and
|
||||
// accounts_server.js.
|
||||
|
||||
Meteor.userId = function () {
|
||||
return Accounts.userId();
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Get the current user record, or `null` if no user is logged in. A reactive data source.
|
||||
* @locus Anywhere but publish functions
|
||||
*/
|
||||
Meteor.user = function () {
|
||||
return Accounts.user();
|
||||
};
|
||||
|
||||
// how long (in days) until a login token expires
|
||||
var DEFAULT_LOGIN_EXPIRATION_DAYS = 90;
|
||||
@@ -23,6 +75,9 @@ CONNECTION_CLOSE_DELAY_MS = 10 * 1000;
|
||||
// Set up config for the accounts system. Call this on both the client
|
||||
// and the server.
|
||||
//
|
||||
// Note that this method gets overridden on AccountsServer.prototype, but
|
||||
// the overriding method calls the overridden method.
|
||||
//
|
||||
// XXX we should add some enforcement that this is called on both the
|
||||
// client and the server. Otherwise, a user can
|
||||
// 'forbidClientAccountCreation' only on the client and while it looks
|
||||
@@ -52,7 +107,9 @@ CONNECTION_CLOSE_DELAY_MS = 10 * 1000;
|
||||
* @param {Number} options.loginExpirationInDays The number of days from when a user logs in until their token expires and they are logged out. Defaults to 90. Set to `null` to disable login expiration.
|
||||
* @param {String} options.oauthSecretKey When using the `oauth-encryption` package, the 16 byte key using to encrypt sensitive account credentials in the database, encoded in base64. This option may only be specifed on the server. See packages/oauth-encryption/README.md for details.
|
||||
*/
|
||||
Accounts.config = function(options) {
|
||||
Ap.config = function (options) {
|
||||
var self = this;
|
||||
|
||||
// We don't want users to accidentally only call Accounts.config on the
|
||||
// client, where some of the options will have partial effects (eg removing
|
||||
// the "create account" button from accounts-ui if forbidClientAccountCreation
|
||||
@@ -91,21 +148,19 @@ Accounts.config = function(options) {
|
||||
// set values in Accounts._options
|
||||
_.each(VALID_KEYS, function (key) {
|
||||
if (key in options) {
|
||||
if (key in Accounts._options) {
|
||||
if (key in self._options) {
|
||||
throw new Error("Can't set `" + key + "` more than once");
|
||||
} else {
|
||||
Accounts._options[key] = options[key];
|
||||
}
|
||||
self._options[key] = options[key];
|
||||
}
|
||||
});
|
||||
|
||||
// If the user set loginExpirationInDays to null, then we need to clear the
|
||||
// timer that periodically expires tokens.
|
||||
if (Meteor.isServer)
|
||||
maybeStopExpireTokensInterval();
|
||||
};
|
||||
|
||||
if (Meteor.isClient) {
|
||||
Ap._initConnection = function (options) {
|
||||
if (! Meteor.isClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The connection used by the Accounts system. This is the connection
|
||||
// that will get logged in by Meteor.login(), and this is the
|
||||
// connection whose login state will be reflected by Meteor.userId().
|
||||
@@ -113,10 +168,13 @@ if (Meteor.isClient) {
|
||||
// It would be much preferable for this to be in accounts_client.js,
|
||||
// but it has to be here because it's needed to create the
|
||||
// Meteor.users collection.
|
||||
Accounts.connection = Meteor.connection;
|
||||
|
||||
if (typeof __meteor_runtime_config__ !== "undefined" &&
|
||||
__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL) {
|
||||
if (options.connection) {
|
||||
this.connection = options.connection;
|
||||
} else if (options.ddpUrl) {
|
||||
this.connection = DDP.connect(options.ddpUrl);
|
||||
} else if (typeof __meteor_runtime_config__ !== "undefined" &&
|
||||
__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL) {
|
||||
// Temporary, internal hook to allow the server to point the client
|
||||
// to a different authentication server. This is for a very
|
||||
// particular use case that comes up when implementing a oauth
|
||||
@@ -124,83 +182,62 @@ if (Meteor.isClient) {
|
||||
//
|
||||
// We will eventually provide a general way to use account-base
|
||||
// against any DDP connection, not just one special one.
|
||||
Accounts.connection = DDP.connect(
|
||||
__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL)
|
||||
this.connection =
|
||||
DDP.connect(__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL);
|
||||
} else {
|
||||
this.connection = Meteor.connection;
|
||||
}
|
||||
}
|
||||
|
||||
// Users table. Don't use the normal autopublish, since we want to hide
|
||||
// some fields. Code to autopublish this is in accounts_server.js.
|
||||
// XXX Allow users to configure this collection name.
|
||||
|
||||
/**
|
||||
* @summary A [Mongo.Collection](#collections) containing user documents.
|
||||
* @locus Anywhere
|
||||
* @type {Mongo.Collection}
|
||||
*/
|
||||
Meteor.users = new Mongo.Collection("users", {
|
||||
_preventAutopublish: true,
|
||||
connection: Meteor.isClient ? Accounts.connection : Meteor.connection
|
||||
});
|
||||
// There is an allow call in accounts_server that restricts this
|
||||
// collection.
|
||||
};
|
||||
|
||||
// loginServiceConfiguration and ConfigError are maintained for backwards compatibility
|
||||
Meteor.startup(function () {
|
||||
var ServiceConfiguration =
|
||||
Package['service-configuration'].ServiceConfiguration;
|
||||
Accounts.loginServiceConfiguration = ServiceConfiguration.configurations;
|
||||
Accounts.ConfigError = ServiceConfiguration.ConfigError;
|
||||
Ap.loginServiceConfiguration = ServiceConfiguration.configurations;
|
||||
Ap.ConfigError = ServiceConfiguration.ConfigError;
|
||||
});
|
||||
|
||||
// Thrown when the user cancels the login process (eg, closes an oauth
|
||||
// popup, declines retina scan, etc)
|
||||
Accounts.LoginCancelledError = function(description) {
|
||||
Ap.LoginCancelledError = function (description) {
|
||||
Error.apply(this, arguments);
|
||||
this.message = description;
|
||||
};
|
||||
|
||||
// This is used to transmit specific subclass errors over the wire. We should
|
||||
// come up with a more generic way to do this (eg, with some sort of symbolic
|
||||
// error code rather than a number).
|
||||
Accounts.LoginCancelledError.numericError = 0x8acdc2f;
|
||||
Accounts.LoginCancelledError.prototype = new Error();
|
||||
Accounts.LoginCancelledError.prototype.name = 'Accounts.LoginCancelledError';
|
||||
Ap.LoginCancelledError.numericError = 0x8acdc2f;
|
||||
var LCEp = Ap.LoginCancelledError.prototype = Object.create(Error.prototype);
|
||||
LCEp.constructor = Ap.LoginCancelledError;
|
||||
LCEp.name = 'Accounts.LoginCancelledError';
|
||||
|
||||
getTokenLifetimeMs = function () {
|
||||
return (Accounts._options.loginExpirationInDays ||
|
||||
Ap._getTokenLifetimeMs = function () {
|
||||
return (this._options.loginExpirationInDays ||
|
||||
DEFAULT_LOGIN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000;
|
||||
};
|
||||
|
||||
Accounts._tokenExpiration = function (when) {
|
||||
Ap._tokenExpiration = function (when) {
|
||||
// We pass when through the Date constructor for backwards compatibility;
|
||||
// `when` used to be a number.
|
||||
return new Date((new Date(when)).getTime() + getTokenLifetimeMs());
|
||||
return new Date((new Date(when)).getTime() + this._getTokenLifetimeMs());
|
||||
};
|
||||
|
||||
Accounts._tokenExpiresSoon = function (when) {
|
||||
var minLifetimeMs = .1 * getTokenLifetimeMs();
|
||||
Ap._tokenExpiresSoon = function (when) {
|
||||
var minLifetimeMs = .1 * this._getTokenLifetimeMs();
|
||||
var minLifetimeCapMs = MIN_TOKEN_LIFETIME_CAP_SECS * 1000;
|
||||
if (minLifetimeMs > minLifetimeCapMs)
|
||||
minLifetimeMs = minLifetimeCapMs;
|
||||
return new Date() > (new Date(when) - minLifetimeMs);
|
||||
};
|
||||
|
||||
// Callback exceptions are printed with Meteor._debug and ignored.
|
||||
onLoginHook = new Hook({
|
||||
debugPrintExceptions: "onLogin callback"
|
||||
});
|
||||
onLoginFailureHook = new Hook({
|
||||
debugPrintExceptions: "onLoginFailure callback"
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @summary Register a callback to be called after a login attempt succeeds.
|
||||
* @locus Anywhere
|
||||
* @param {Function} func The callback to be called when login is successful.
|
||||
*/
|
||||
Accounts.onLogin = function (func) {
|
||||
return onLoginHook.register(func);
|
||||
Ap.onLogin = function (func) {
|
||||
return this._onLoginHook.register(func);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -208,6 +245,6 @@ Accounts.onLogin = function (func) {
|
||||
* @locus Anywhere
|
||||
* @param {Function} func The callback to be called after the login has failed.
|
||||
*/
|
||||
Accounts.onLoginFailure = function (func) {
|
||||
return onLoginFailureHook.register(func);
|
||||
Ap.onLoginFailure = function (func) {
|
||||
return this._onLoginFailureHook.register(func);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user