diff --git a/History.md b/History.md index dbe292bf58..5b738c59b4 100644 --- a/History.md +++ b/History.md @@ -2,21 +2,19 @@ #### Highlights -* Node.js update to 14.17.0 from 12.22.1 :tada: +* Node.js update to 14.17.0 from 12.22.1 🎉 -* Typescript update to 4.2.4 +* Typescript update to [4.3.2](https://devblogs.microsoft.com/typescript/announcing-typescript-4-3/) -* New env variable `METEOR_TOOL_ENABLE_REIFY_RUNTIME_CACHE` to improve runtime performance on restarts. +* Packages had their backward compatibility to before Meteor 1.0 removed. See bellow for more details. -* New flag `--platforms` has been added to the `build` command to specify the platform you want to build for. `meteor build . --platforms=android`. - -* Skeletons dependencies updated to latest version - -### Breaking changes +### Summary of breaking changes - As Node.js version was upgraded to a new major version we recommend that you review if your npm dependencies are compatible with Node.js 14. - If we receive reports from breaking changes we are going to list them here but so far we are not aware of any. - We recommend that you read Node.js [release notes](https://nodejs.org/en/blog/release/v14.0.0/) though. + +- Accounts have undergone some major changes. See bellow for more details. ### Migration steps @@ -26,12 +24,37 @@ #### Meteor Version Release * `meteor-tool@2.3` - - Node.js update to 14.17.0 from 12.22.1 :tada: + - Node.js update to 14.17.0 from 12.22.1 🎉 - This is a major upgrade in Node.js. See the [release notes](https://nodejs.org/en/blog/release/v14.0.0/) for more details. - - npm update to 6.14.13. + - `npm` update to 6.14.13. + - `fibers` has been updated to v5.0.0. + - `promise` has been updated to v8.1.0. + - `underscore` has been updated to v1.11.0. + - `node-gyp` has been updated to v8.0.0. + - `node-pre-gyp` has been updated to v0.15.0. + - `@babel/runtime` has been updated to v7.13.17. + - `request` has been updated to v2.88.2. + - `uuid` has been updated to v3.4.0. + - `graceful-fs` has been updated to v4.2.6. + - `tar` has been updated to v2.2.2. + - `sqlite3` has been updated to v5.0.2. + - `http-proxy` has been updated to v1.18.1. + - `wordwrap` has been updated to v1.0.0. + - `moment` has been updated to v2.29.1. + - `glob` has been updated to v7.1.6. + - `split2` has been updated to v3.2.2. + - `optimism` has been updated to v0.12.2. + - `@wry/context` has been updated to v0.5.2. + - `lru-cache` has been updated to v4.1.5. + - `anser` has been updated to v2.0.1. + - `xmlbuilder2` has been updated to v1.8.1. + - `ws` has been updated to v7.4.5. - New env variable `METEOR_TOOL_ENABLE_REIFY_RUNTIME_CACHE` that improves restarts by caching reify data. This new cache should help with runtime performance when restarting server, but it is not yet ready for general use. By default it is disabled but you can enable it setting `true` in this env var. Learn more in the [PR](https://github.com/meteor/meteor/pull/11400). - New flag `--platforms` has been added to the `build` command to specify the platform you want to build for. `meteor build . --platforms=android`. This is useful for example when you are not using a MacOS and you want to build your app only for Android. Also to save time on CI not building all the platforms all the time. See [PR](https://github.com/meteor/meteor/pull/11437) for details. - The undocumented environment variable `DDP_DEFAULT_CONNECTION_URL` behavior has changed. Setting `DDP_DEFAULT_CONNECTION_URL` when running the server (development: `meteor run` or production: `node main.js`) sets the default DDP server value for meteor. But this did not work for `cordova` apps. Now you can define the `cordova` app default DDP server value by setting `DDP_DEFAULT_CONNECTION_URL` when building (`meteor build`). + - New env variable `METEOR_TOOL_ENABLE_REIFY_RUNTIME_CACHE` to improve runtime performance on restarts. + - New flag `--platforms` has been added to the `build` command to specify the platform you want to build for. `meteor build . --platforms=android`. + - Skeletons dependencies updated to latest version * `launch-screen@1.3.0` - Removes LaunchScreen from web clients. @@ -46,6 +69,49 @@ * `email@2.1.0` - Updates `nodemailer` to `6.6.0` and it now adds `charset=utf-8` to `text/plain` messages by default. +* `server-render@0.4.0` + - Updated npm dependencies + +### Breaking changes +* Removed deprecated `mobile-port` flag + +* Removed deprecated `raw` name from `isobuild` + +* Removed deprecated package API method names `Package.on_use`, `Package.on_test`, `Package._transitional_registerBuildPlugin` and `api.add_files`, if you haven't till now, please use the current camel case versions + +* `accounts-base@2.0.0` + - Deprecated backward compatibility function `logoutOtherClients` has been removed + +* `accounts-password@2.0.0` + - Deprecated backward compatibility functionality for `SRP` passwords from pre-Meteor 1.0 days + - Enroll account workflow has been separated from reset password workflow (the enrollment token records are now stored in a separate db field `services.password.enroll`). + +* `ddp-client@2.5.0` + - Removed backward compatibility method names for Meteor before 1.0 + +* `ddp-server@2.4.0` + - Removed backward compatibility method names for Meteor before 1.0 + - Added support for this.unblock() in Meteor.publish() context + +* `meteor-base@2.0.0` + - Removed `livedata` dependency which was there for packages build for 0.9.0 + +* `minimongo@1.7.0` + - Removed the `rewind` method that was noop for compatibility with Meteor 0.8.1 + +* `mongo@1.12.0` + - Removed the `rewind` method that was noop for compatibility with Meteor 0.8.1 + +* `oauth@2.0.0` + - Removed `OAuth.initiateLogin` and other functionality like the addition of `?close` in return URI for deprecated OAuth flow pre Meteor 1.0 + +* `markdown@2.0.0` + - Use lazy imports to prevent it from being added to the initial bundle + - Added deprecation flag + +* `socket-stream-client@0.4.0` + - Remove IE8 checks + #### Independent Releases * `ddp-server@2.3.3` @@ -77,6 +143,12 @@ * `http@1.4.4` - Used the new deprecation package flag instead of loud console warning. + +* `logic-solver@2.0.8` + - Fixed `package.js` to use current `api` method calls. + +* `socket-stream-client@0.3.3` + - Update `faye-websocket` dependency to v0.11.4. ## v2.2, 2021-04-15 diff --git a/meteor b/meteor index 856b8c95e7..896dd91d55 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=14.17.0.0 +BUNDLE_VERSION=14.17.0.3 # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. diff --git a/packages/accounts-base/accounts_common.js b/packages/accounts-base/accounts_common.js index 7e3e581f25..5a14d071ba 100644 --- a/packages/accounts-base/accounts_common.js +++ b/packages/accounts-base/accounts_common.js @@ -168,7 +168,7 @@ export class AccountsCommon { * @param {Number} options.passwordEnrollTokenExpirationInDays The number of days from when a link to set initial password is sent until token expires and user can't set password with the link anymore. Defaults to 30. * @param {Number} options.passwordEnrollTokenExpiration The number of milliseconds from when a link to set initial password is sent until token expires and user can't set password with the link anymore. If `passwordEnrollTokenExpirationInDays` is set, it takes precedent. * @param {Boolean} options.ambiguousErrorMessages Return ambiguous error messages from login failures to prevent user enumeration. Defaults to false. - * @param {MongoFieldSpecifier} options.defaultFieldSelector To exclude by default large custom fields from `Meteor.user()` and `Meteor.findUserBy...()` functions when called without a field selector, and all `onLogin`, `onLoginFailure` and `onLogout` callbacks. Example: `Accounts.config({ defaultFieldSelector: { myBigArray: 0 }})`. + * @param {MongoFieldSpecifier} options.defaultFieldSelector To exclude by default large custom fields from `Meteor.user()` and `Meteor.findUserBy...()` functions when called without a field selector, and all `onLogin`, `onLoginFailure` and `onLogout` callbacks. Example: `Accounts.config({ defaultFieldSelector: { myBigArray: 0 }})`. Beware when using this. If, for instance, you do not include `email` when excluding the fields, you can have problems with functions like `forgotPassword` that will break because they won't have the required data available. It's recommend that you always keep the fields `_id`, `username`, and `email`. */ config(options) { // We don't want users to accidentally only call Accounts.config on the diff --git a/packages/accounts-base/accounts_server.js b/packages/accounts-base/accounts_server.js index 89e5f36d3b..722e469611 100644 --- a/packages/accounts-base/accounts_server.js +++ b/packages/accounts-base/accounts_server.js @@ -569,62 +569,6 @@ export class AccountsServer extends AccountsCommon { this.setUserId(null); }; - // Delete all the current user's tokens and close all open connections logged - // in as this user. Returns a fresh new login token that this client can - // use. Tests set Accounts._noConnectionCloseDelayForTest to delete tokens - // immediately instead of using a delay. - // - // XXX COMPAT WITH 0.7.2 - // This single `logoutOtherClients` method has been replaced with two - // methods, one that you call to get a new token, and another that you - // call to remove all tokens except your own. The new design allows - // clients to know when other clients have actually been logged - // out. (The `logoutOtherClients` method guarantees the caller that - // the other clients will be logged out at some point, but makes no - // guarantees about when.) This method is left in for backwards - // compatibility, especially since application code might be calling - // this method directly. - // - // @returns {Object} Object with token and tokenExpires keys. - methods.logoutOtherClients = function () { - const user = accounts.users.findOne(this.userId, { - fields: { - "services.resume.loginTokens": true - } - }); - if (user) { - // Save the current tokens in the database to be deleted in - // CONNECTION_CLOSE_DELAY_MS ms. This gives other connections in the - // caller's browser time to find the fresh token in localStorage. We save - // the tokens in the database in case we crash before actually deleting - // them. - const tokens = user.services.resume.loginTokens; - const newToken = accounts._generateStampedLoginToken(); - accounts.users.update(this.userId, { - $set: { - "services.resume.loginTokensToDelete": tokens, - "services.resume.haveLoginTokensToDelete": true - }, - $push: { "services.resume.loginTokens": accounts._hashStampedToken(newToken) } - }); - Meteor.setTimeout(() => { - // The observe on Meteor.users will take care of closing the connections - // associated with `tokens`. - accounts._deleteSavedTokensForUser(this.userId, tokens); - }, accounts._noConnectionCloseDelayForTest ? 0 : - CONNECTION_CLOSE_DELAY_MS); - // We do not set the login token on this connection, but instead the - // observe closes the connection and the client will reconnect with the - // new token. - return { - token: newToken.token, - tokenExpires: accounts._tokenExpiration(newToken.when) - }; - } else { - throw new Meteor.Error("You are not logged in."); - } - }; - // Generates a new login token with the same expiration as the // connection's current token and saves it to the database. Associates // the connection with this new token and returns it. Throws an error @@ -1029,7 +973,7 @@ export class AccountsServer extends AccountsCommon { (new Date(new Date() - tokenLifetimeMs)); const tokenFilter = { - "services.password.reset.reason": "enroll" + "services.password.enroll.reason": "enroll" }; expirePasswordToken(this, oldestValidDate, tokenFilter, userId); @@ -1462,20 +1406,42 @@ const expirePasswordToken = ( tokenFilter, userId ) => { + // boolean value used to determine if this method was called from enroll account workflow + let isEnroll = false; const userFilter = userId ? {_id: userId} : {}; - const resetRangeOr = { + // check if this method was called from enroll account workflow + if(tokenFilter['services.password.enroll.reason']) { + isEnroll = true; + } + let resetRangeOr = { $or: [ { "services.password.reset.when": { $lt: oldestValidDate } }, { "services.password.reset.when": { $lt: +oldestValidDate } } ] }; + if(isEnroll) { + resetRangeOr = { + $or: [ + { "services.password.enroll.when": { $lt: oldestValidDate } }, + { "services.password.enroll.when": { $lt: +oldestValidDate } } + ] + }; + } const expireFilter = { $and: [tokenFilter, resetRangeOr] }; + if(isEnroll) { + accounts.users.update({...userFilter, ...expireFilter}, { + $unset: { + "services.password.enroll": "" + } + }, { multi: true }); + } else { + accounts.users.update({...userFilter, ...expireFilter}, { + $unset: { + "services.password.reset": "" + } + }, { multi: true }); + } - accounts.users.update({...userFilter, ...expireFilter}, { - $unset: { - "services.password.reset": "" - } - }, { multi: true }); }; const setExpireTokensInterval = accounts => { diff --git a/packages/accounts-base/accounts_tests.js b/packages/accounts-base/accounts_tests.js index b711bc9a9c..1d9475cb23 100644 --- a/packages/accounts-base/accounts_tests.js +++ b/packages/accounts-base/accounts_tests.js @@ -1,6 +1,7 @@ import { URL } from 'meteor/url'; import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; +import { Random } from 'meteor/random'; Meteor.methods({ getCurrentLoginToken: function () { @@ -111,6 +112,32 @@ Tinytest.add('accounts - updateOrCreateUserFromExternalService - Facebook', test Meteor.users.remove(uid1); }); +Tinytest.add('accounts - updateOrCreateUserFromExternalService - Meteor Developer', test => { + const developerId = Random.id(); + const uid1 = Accounts.updateOrCreateUserFromExternalService( + 'meteor-developer', + { id: developerId, username: 'meteor-developer' }, + { profile: { name: 'meteor-developer' } } + ).id; + const users1 = Meteor.users.find({ 'services.meteor-developer.id': developerId }).fetch(); + test.length(users1, 1); + test.equal(users1[0].profile.name, 'meteor-developer'); + + const uid2 = Accounts.updateOrCreateUserFromExternalService( + 'meteor-developer', + { id: developerId, username: 'meteor-developer' }, + { profile: { name: 'meteor-developer', username: 'developer' } } + ).id; + test.equal(uid1, uid2); + const users2 = Meteor.users.find({ 'services.meteor-developer.id': developerId }).fetch(); + test.length(users2, 1); + test.equal(users1[0].profile.name, 'meteor-developer'); + test.equal(users1[0].profile.username, undefined); + + // cleanup + Meteor.users.remove(uid1); +}); + Tinytest.add('accounts - updateOrCreateUserFromExternalService - Weibo', test => { const weiboId1 = Random.id(); const weiboId2 = Random.id(); diff --git a/packages/accounts-base/package.js b/packages/accounts-base/package.js index d494e1b3ba..df6d363282 100644 --- a/packages/accounts-base/package.js +++ b/packages/accounts-base/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "A user account system", - version: "1.9.0", + version: "2.0.0-beta230.3", }); Package.onUse(api => { diff --git a/packages/accounts-facebook/package.js b/packages/accounts-facebook/package.js index 6240e43bf7..d65c645dce 100644 --- a/packages/accounts-facebook/package.js +++ b/packages/accounts-facebook/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Facebook accounts", - version: "1.3.2", + version: "1.3.0-beta.2", }); Package.onUse(api => { diff --git a/packages/accounts-github/package.js b/packages/accounts-github/package.js index 969d33e692..7991d48345 100644 --- a/packages/accounts-github/package.js +++ b/packages/accounts-github/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Login service for Github accounts', - version: '1.4.3', + version: '1.5.0-beta230.3', }); Package.onUse(api => { diff --git a/packages/accounts-google/package.js b/packages/accounts-google/package.js index 7ec6309d8c..c513764091 100644 --- a/packages/accounts-google/package.js +++ b/packages/accounts-google/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Google accounts", - version: "1.3.3", + version: "1.4.0-beta230.3", }); Package.onUse(api => { diff --git a/packages/accounts-meetup/package.js b/packages/accounts-meetup/package.js index 9cc3b51cc1..66577af879 100644 --- a/packages/accounts-meetup/package.js +++ b/packages/accounts-meetup/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Login service for Meetup accounts', - version: '1.4.2', + version: '1.5.0-beta230.3', }); Package.onUse(api => { diff --git a/packages/accounts-meteor-developer/package.js b/packages/accounts-meteor-developer/package.js index 886d1b5c99..387d7521be 100644 --- a/packages/accounts-meteor-developer/package.js +++ b/packages/accounts-meteor-developer/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Login service for Meteor developer accounts', - version: '1.4.2', + version: '1.5.0-beta230.3', }); Package.onUse(api => { diff --git a/packages/accounts-oauth/package.js b/packages/accounts-oauth/package.js index fb61a4f77e..ee24702e20 100644 --- a/packages/accounts-oauth/package.js +++ b/packages/accounts-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth-based login services", - version: "1.2.0", + version: "1.3.0-beta230.3", }); Package.onUse(api => { diff --git a/packages/accounts-password/package.js b/packages/accounts-password/package.js index 9109e5cc93..06dd2eee16 100644 --- a/packages/accounts-password/package.js +++ b/packages/accounts-password/package.js @@ -5,7 +5,7 @@ Package.describe({ // 2.2.x in the future. The version was also bumped to 2.0.0 temporarily // during the Meteor 1.5.1 release process, so versions 2.0.0-beta.2 // through -beta.5 and -rc.0 have already been published. - version: "1.7.1" + version: "2.0.0-beta230.3" }); Package.onUse(api => { @@ -13,7 +13,6 @@ Package.onUse(api => { api.use([ 'accounts-base', - 'srp', 'sha', 'ejson', 'ddp' @@ -33,7 +32,7 @@ Package.onUse(api => { }); Package.onTest(api => { - api.use(['accounts-password', 'tinytest', 'test-helpers', 'tracker', + api.use(['accounts-password', 'sha', 'tinytest', 'test-helpers', 'tracker', 'accounts-base', 'random', 'email', 'check', 'ddp', 'ecmascript']); api.addFiles('password_tests_setup.js', 'server'); api.addFiles('password_tests.js', ['client', 'server']); diff --git a/packages/accounts-password/password_client.js b/packages/accounts-password/password_client.js index 871393127a..d4d53c04d3 100644 --- a/packages/accounts-password/password_client.js +++ b/packages/accounts-password/password_client.js @@ -43,27 +43,7 @@ Meteor.loginWithPassword = (selector, password, callback) => { password: Accounts._hashPassword(password) }], userCallback: (error, result) => { - if (error && error.error === 400 && - error.reason === 'old password format') { - // The "reason" string should match the error thrown in the - // password login handler in password_server.js. - - // XXX COMPAT WITH 0.8.1.3 - // If this user's last login was with a previous version of - // Meteor that used SRP, then the server throws this error to - // indicate that we should try again. The error includes the - // user's SRP identity. We provide a value derived from the - // identity and the password to prove to the server that we know - // the password without requiring a full SRP flow, as well as - // SHA256(password), which the server bcrypts and stores in - // place of the old SRP information for this user. - srpUpgradePath({ - upgradeError: error, - userSelector: selector, - plaintextPassword: password - }, callback); - } - else if (error) { + if (error) { reportError(error, callback); } else { callback && callback(); @@ -77,35 +57,6 @@ Accounts._hashPassword = password => ({ algorithm: "sha-256" }); - -// XXX COMPAT WITH 0.8.1.3 -// The server requested an upgrade from the old SRP password format, -// so supply the needed SRP identity to login. Options: -// - upgradeError: the error object that the server returned to tell -// us to upgrade from SRP to bcrypt. -// - userSelector: selector to retrieve the user object -// - plaintextPassword: the password as a string -const srpUpgradePath = (options, callback) => { - let details; - try { - details = EJSON.parse(options.upgradeError.details); - } catch (e) {} - if (!(details && details.format === 'srp')) { - reportError( - new Meteor.Error(400, "Password is old. Please reset your " + - "password."), callback); - } else { - Accounts.callLoginMethod({ - methodArguments: [{ - user: options.userSelector, - srp: SHA256(`${details.identity}:${options.plaintextPassword}`), - password: Accounts._hashPassword(options.plaintextPassword) - }], - userCallback: callback - }); - } -}; - // Attempt to log in as a new user. /** @@ -172,30 +123,10 @@ Accounts.changePassword = (oldPassword, newPassword, callback) => { [oldPassword ? Accounts._hashPassword(oldPassword) : null, Accounts._hashPassword(newPassword)], (error, result) => { - if (error || !result) { - if (error && error.error === 400 && - error.reason === 'old password format') { - // XXX COMPAT WITH 0.8.1.3 - // The server is telling us to upgrade from SRP to bcrypt, as - // in Meteor.loginWithPassword. - srpUpgradePath({ - upgradeError: error, - userSelector: { id: Meteor.userId() }, - plaintextPassword: oldPassword - }, err => { - if (err) { - reportError(err, callback); - } else { - // Now that we've successfully migrated from srp to - // bcrypt, try changing the password again. - Accounts.changePassword(oldPassword, newPassword, callback); - } - }); - } else { - // A normal error, not an error telling us to upgrade to bcrypt - reportError( - error || new Error("No result from changePassword."), callback); - } + if (error || !result) { + // A normal error, not an error telling us to upgrade to bcrypt + reportError( + error || new Error("No result from changePassword."), callback); } else { callback && callback(); } diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js index e98de6e691..589b797f61 100644 --- a/packages/accounts-password/password_server.js +++ b/packages/accounts-password/password_server.js @@ -8,9 +8,7 @@ const bcryptCompare = Meteor.wrapAsync(bcrypt.compare); const getUserById = (id, options) => Meteor.users.findOne(id, Accounts._addDefaultFieldSelector(options)); // User records have a 'services.password.bcrypt' field on them to hold -// their hashed passwords (unless they have a 'services.password.srp' -// field, in which case they will be upgraded to bcrypt the next time -// they log in). +// their hashed passwords. // // When the client sends a password to the server, it can either be a // string (the plaintext password) or an object with keys 'digest' and @@ -276,12 +274,10 @@ const userQueryValidator = Match.Where(user => { return true; }); -const passwordValidator = Match.OneOf( - Match.Where(str => Match.test(str, String) && str.length <= Meteor.settings?.packages?.accounts?.passwordMaxLength || 256), { - digest: Match.Where(str => Match.test(str, String) && str.length === 64), - algorithm: Match.OneOf('sha-256') - } -); +const passwordValidator = { + digest: Match.Where(str => Match.test(str, String) && str.length === 64), + algorithm: Match.OneOf('sha-256') +}; // Handler to login with a password. // @@ -298,7 +294,7 @@ const passwordValidator = Match.OneOf( // Note that neither password option is secure without SSL. // Accounts.registerLoginHandler("password", options => { - if (! options.password || options.srp) + if (!options.password) return undefined; // don't handle check(options, { @@ -316,116 +312,16 @@ Accounts.registerLoginHandler("password", options => { } if (!user.services || !user.services.password || - !(user.services.password.bcrypt || user.services.password.srp)) { + !user.services.password.bcrypt) { handleError("User has no password set"); } - if (!user.services.password.bcrypt) { - if (typeof options.password === "string") { - // The client has presented a plaintext password, and the user is - // not upgraded to bcrypt yet. We don't attempt to tell the client - // to upgrade to bcrypt, because it might be a standalone DDP - // client doesn't know how to do such a thing. - const verifier = user.services.password.srp; - const newVerifier = SRP.generateVerifier(options.password, { - identity: verifier.identity, salt: verifier.salt}); - - if (verifier.verifier !== newVerifier.verifier) { - return { - userId: Accounts._options.ambiguousErrorMessages ? null : user._id, - error: handleError("Incorrect password", false) - }; - } - - return {userId: user._id}; - } else { - // Tell the client to use the SRP upgrade process. - throw new Meteor.Error(400, "old password format", EJSON.stringify({ - format: 'srp', - identity: user.services.password.srp.identity - })); - } - } - return checkPassword( user, options.password ); }); -// Handler to login using the SRP upgrade path. To use this login -// handler, the client must provide: -// - srp: H(identity + ":" + password) -// - password: a string or an object with properties 'digest' and 'algorithm' -// -// We use `options.srp` to verify that the client knows the correct -// password without doing a full SRP flow. Once we've checked that, we -// upgrade the user to bcrypt and remove the SRP information from the -// user document. -// -// The client ends up using this login handler after trying the normal -// login handler (above), which throws an error telling the client to -// try the SRP upgrade path. -// -// XXX COMPAT WITH 0.8.1.3 -Accounts.registerLoginHandler("password", options => { - if (!options.srp || !options.password) { - return undefined; // don't handle - } - - check(options, { - user: userQueryValidator, - srp: String, - password: passwordValidator - }); - - const user = Accounts._findUserByQuery(options.user, {fields: { - services: 1, - ...Accounts._checkPasswordUserFields, - }}); - if (!user) { - handleError("User not found"); - } - - // Check to see if another simultaneous login has already upgraded - // the user record to bcrypt. - if (user.services && user.services.password && user.services.password.bcrypt) { - return checkPassword(user, options.password); - } - - if (!(user.services && user.services.password && user.services.password.srp)) { - handleError("User has no password set"); - } - - const v1 = user.services.password.srp.verifier; - const v2 = SRP.generateVerifier( - null, - { - hashedIdentityAndPassword: options.srp, - salt: user.services.password.srp.salt - } - ).verifier; - if (v1 !== v2) { - return { - userId: Accounts._options.ambiguousErrorMessages ? null : user._id, - error: handleError("Incorrect password", false) - }; - } - - // Upgrade to bcrypt on successful login. - const salted = hashPassword(options.password); - Meteor.users.update( - user._id, - { - $unset: { 'services.password.srp': 1 }, - $set: { 'services.password.bcrypt': salted } - } - ); - - return {userId: user._id}; -}); - - /// /// CHANGING /// @@ -471,18 +367,6 @@ Accounts.setUsername = (userId, newUsername) => { // Let the user change their own password if they know the old // password. `oldPassword` and `newPassword` should be objects with keys // `digest` and `algorithm` (representing the SHA256 of the password). -// -// XXX COMPAT WITH 0.8.1.3 -// Like the login method, if the user hasn't been upgraded from SRP to -// bcrypt yet, then this method will throw an 'old password format' -// error. The client should call the SRP upgrade login handler and then -// retry this method again. -// -// UNLIKE the login method, there is no way to avoid getting SRP upgrade -// errors thrown. The reasoning for this is that clients using this -// method directly will need to be updated anyway because we no longer -// support the SRP flow that they would have been doing to use this -// method previously. Meteor.methods({changePassword: function (oldPassword, newPassword) { check(oldPassword, passwordValidator); check(newPassword, passwordValidator); @@ -499,18 +383,10 @@ Meteor.methods({changePassword: function (oldPassword, newPassword) { handleError("User not found"); } - if (!user.services || !user.services.password || - (!user.services.password.bcrypt && !user.services.password.srp)) { + if (!user.services || !user.services.password || !user.services.password.bcrypt) { handleError("User has no password set"); } - if (! user.services.password.bcrypt) { - throw new Meteor.Error(400, "old password format", EJSON.stringify({ - format: 'srp', - identity: user.services.password.srp.identity - })); - } - const result = checkPassword(user, oldPassword); if (result.error) { throw result.error; @@ -550,6 +426,9 @@ Meteor.methods({changePassword: function (oldPassword, newPassword) { * @importFromPackage accounts-base */ Accounts.setPassword = (userId, newPlaintextPassword, options) => { + check(userId, String) + check(newPlaintextPassword, Match.Where(str => Match.test(str, String) && str.length <= Meteor.settings?.packages?.accounts?.passwordMaxLength || 256)) + check(options, Match.Maybe({ logout: Boolean })) options = { logout: true , ...options }; const user = getUserById(userId, {fields: {_id: 1}}); @@ -559,7 +438,6 @@ Accounts.setPassword = (userId, newPlaintextPassword, options) => { const update = { $unset: { - 'services.password.srp': 1, // XXX COMPAT WITH 0.8.1.3 'services.password.reset': 1 }, $set: {'services.password.bcrypt': hashPassword(newPlaintextPassword)} @@ -646,15 +524,27 @@ Accounts.generateResetToken = (userId, email, reason, extraTokenData) => { if (extraTokenData) { Object.assign(tokenRecord, extraTokenData); + } + // if this method is called from the enroll account work-flow then + // store the token record in 'services.password.enroll' db field + // else store the token record in in 'services.password.reset' db field + if(reason === 'enrollAccount') { + Meteor.users.update({_id: user._id}, { + $set : { + 'services.password.enroll': tokenRecord + } + }); + } else { + Meteor.users.update({_id: user._id}, { + $set : { + 'services.password.reset': tokenRecord + } + }); } - Meteor.users.update({_id: user._id}, {$set: { - 'services.password.reset': tokenRecord - }}); - // before passing to template, update user object with new token Meteor._ensure(user, 'services', 'password').reset = tokenRecord; - + Meteor._ensure(user, 'services', 'password').enroll = tokenRecord; return {email, user, token}; }; @@ -823,17 +713,38 @@ Meteor.methods({resetPassword: function (...args) { check(token, String); check(newPassword, passwordValidator); - const user = Meteor.users.findOne( + let user = Meteor.users.findOne( {"services.password.reset.token": token}, {fields: { services: 1, emails: 1, }} ); + + let isEnroll = false; + // if token is in services.password.reset db field implies + // this method is was not called from enroll account workflow + // else this method is called from enroll account workflow + if(!user) { + user = Meteor.users.findOne( + {"services.password.enroll.token": token}, + {fields: { + services: 1, + emails: 1, + }} + ); + isEnroll = true; + } if (!user) { throw new Meteor.Error(403, "Token expired"); } - const { when, reason, email } = user.services.password.reset; + let tokenRecord = {}; + if(isEnroll) { + tokenRecord = user.services.password.enroll; + } else { + tokenRecord = user.services.password.reset; + } + const { when, reason, email } = tokenRecord; let tokenLifetimeMs = Accounts._getPasswordResetTokenLifetimeMs(); if (reason === "enroll") { tokenLifetimeMs = Accounts._getPasswordEnrollTokenLifetimeMs(); @@ -847,7 +758,7 @@ Meteor.methods({resetPassword: function (...args) { error: new Meteor.Error(403, "Token has invalid email address") }; - const hashed = hashPassword(newPassword); + const hashed = hashPassword(newPassword); // NOTE: We're about to invalidate tokens on the user, who we might be // logged in as. Make sure to avoid logging ourselves out if this @@ -861,18 +772,31 @@ Meteor.methods({resetPassword: function (...args) { try { // Update the user record by: // - Changing the password to the new one - // - Forgetting about the reset token that was just used + // - Forgetting about the reset token or enroll token that was just used // - Verifying their email, since they got the password reset via email. - const affectedRecords = Meteor.users.update( - { - _id: user._id, - 'emails.address': email, - 'services.password.reset.token': token - }, - {$set: {'services.password.bcrypt': hashed, - 'emails.$.verified': true}, - $unset: {'services.password.reset': 1, - 'services.password.srp': 1}}); + let affectedRecords = {}; + // if reason is enroll then check services.password.enroll.token field for affected records + if(reason === 'enroll') { + affectedRecords = Meteor.users.update( + { + _id: user._id, + 'emails.address': email, + 'services.password.enroll.token': token + }, + {$set: {'services.password.bcrypt': hashed, + 'emails.$.verified': true}, + $unset: {'services.password.enroll': 1 }}); + } else { + affectedRecords = Meteor.users.update( + { + _id: user._id, + 'emails.address': email, + 'services.password.reset.token': token + }, + {$set: {'services.password.bcrypt': hashed, + 'emails.$.verified': true}, + $unset: {'services.password.reset': 1 }}); + } if (affectedRecords !== 1) return { userId: user._id, @@ -1236,3 +1160,5 @@ Meteor.users._ensureIndex('services.email.verificationTokens.token', { unique: true, sparse: true }); Meteor.users._ensureIndex('services.password.reset.token', { unique: true, sparse: true }); +Meteor.users._ensureIndex('services.password.enroll.token', + { unique: true, sparse: true }); diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js index db4071b18d..0bea7d7baf 100644 --- a/packages/accounts-password/password_tests.js +++ b/packages/accounts-password/password_tests.js @@ -1,5 +1,12 @@ Accounts._connectionCloseDelayMsForTests = 1000; +function hashPassword(password) { + return { + digest: SHA256(password), + algorithm: "sha-256" + }; +} + if (Meteor.isServer) { Accounts.removeDefaultRateLimit(); @@ -63,7 +70,7 @@ if (Meteor.isClient) (() => { } const user = Meteor.user(); test.isTrue(user && user.emails.reduce( - (prev, email) => prev || email.address === someEmail, + (prev, email) => prev || email.address === someEmail, false )); }); @@ -72,15 +79,15 @@ if (Meteor.isClient) (() => { test.equal(actualError && actualError.error, expectedError.error); test.equal(actualError && actualError.reason, expectedError.reason); }); - const expectUserNotFound = (test, expect) => + const expectUserNotFound = (test, expect) => expectError(new Meteor.Error(403, "User not found"), test, expect); const waitForLoggedOutStep = (test, expect) => pollUntil( - expect, - () => Meteor.userId() === null, - 10 * 1000, + expect, + () => Meteor.userId() === null, + 10 * 1000, 100 ); - const invalidateLoginsStep = (test, expect) => + const invalidateLoginsStep = (test, expect) => Meteor.call("testInvalidateLogins", 'fail', expect(error => { if (error) { test.fail(error.message); @@ -160,51 +167,6 @@ if (Meteor.isClient) (() => { logoutStep ]); - - testAsyncMulti("passwords - plain text passwords", [ - function (test, expect) { - // setup - this.username = Random.id(); - this.email = `${Random.id()}-intercept@example.com`; - this.password = 'password'; - - // create user with raw password (no API, need to invoke callLoginMethod - // directly) - Accounts.callLoginMethod({ - methodName: 'createUser', - methodArguments: [{username: this.username, password: this.password}], - userCallback: loggedInAs(this.username, test, expect) - }); - }, - logoutStep, - // check can login normally with this password. - function(test, expect) { - Meteor.loginWithPassword({username: this.username}, this.password, - loggedInAs(this.username, test, expect)); - }, - logoutStep, - // plain text password. no API for this, have to invoke callLoginMethod - // directly. - function (test, expect) { - Accounts.callLoginMethod({ - // wrong password - methodArguments: [{user: {username: this.username}, password: 'wrong'}], - userCallback: expect(function (error) { - test.isTrue(error); - test.isFalse(Meteor.user()); - })}); - }, - function (test, expect) { - Accounts.callLoginMethod({ - // right password - methodArguments: [{user: {username: this.username}, - password: this.password}], - userCallback: loggedInAs(this.username, test, expect) - }); - }, - logoutStep - ]); - testAsyncMulti("passwords - logging in with case insensitive username", [ createUserStep, logoutStep, @@ -467,7 +429,7 @@ if (Meteor.isClient) (() => { // change password with blank new password function (test, expect) { test.throws( - () => Accounts.changePassword(this.password, ''), + () => Accounts.changePassword(this.password, ''), /Password may not be empty/ ); }, @@ -513,13 +475,13 @@ if (Meteor.isClient) (() => { function (test, expect) { this.secondConn = DDP.connect(Meteor.absoluteUrl()); this.secondConn.call('login', - { user: { username: this.username }, password: this.password }, + { user: { username: this.username }, password: hashPassword(this.password) }, expect((err, result) => { test.isFalse(err); this.secondConn.setUserId(result.id); test.isTrue(this.secondConn.userId()); - this.secondConn.onReconnect = () => + this.secondConn.onReconnect = () => this.secondConn.apply( 'login', [{ resume: result.token }], @@ -531,8 +493,8 @@ if (Meteor.isClient) (() => { }, function (test, expect) { Accounts.changePassword( - this.password, - this.password2, + this.password, + this.password2, expect(err => test.isFalse(err)) ); }, @@ -540,9 +502,9 @@ if (Meteor.isClient) (() => { // connection gets logged out. function (test, expect) { pollUntil( - expect, - () => this.secondConn.userId() === null, - 10 * 1000, + expect, + () => this.secondConn.userId() === null, + 10 * 1000, 100 ); } @@ -557,14 +519,14 @@ if (Meteor.isClient) (() => { // forgotPassword called on client with blank email function (test, expect) { Accounts.forgotPassword( - { email: this.email }, + { email: this.email }, expect(error => test.isTrue(error)) ); }, // forgotPassword called on client with blank email and no callback. function (test, expect) { test.throws( - () => Accounts.forgotPassword({ email: this.email }), + () => Accounts.forgotPassword({ email: this.email }), /Must pass options\.email/ ); }, @@ -607,14 +569,14 @@ if (Meteor.isClient) (() => { // verifyEmail called on client with blank token function (test, expect) { Accounts.verifyEmail( - this.token, + this.token, expect(error => test.isTrue(error)) ); }, // verifyEmail called on client with blank token and no callback. function (test, expect) { test.throws( - () => Accounts.verifyEmail(this.token), + () => Accounts.verifyEmail(this.token), /Need to pass token/ ); }, @@ -629,8 +591,8 @@ if (Meteor.isClient) (() => { // resetPassword called on client with blank token function (test, expect) { Accounts.resetPassword( - this.token, - this.newPassword, + this.token, + this.newPassword, expect(error => test.isTrue(error)) ); }, @@ -642,8 +604,8 @@ if (Meteor.isClient) (() => { // resetPassword called on client with blank password function (test, expect) { Accounts.resetPassword( - this.token, - this.newPassword, + this.token, + this.newPassword, expect(error => test.isTrue(error)) ); }, @@ -882,10 +844,10 @@ if (Meteor.isClient) (() => { const secondConn = DDP.connect(Meteor.absoluteUrl()); let token; - const expectSecondConnLoggedOut = + const expectSecondConnLoggedOut = expect((err, result) => test.isTrue(err)); - const expectAccountsConnLoggedIn = + const expectAccountsConnLoggedIn = expect((err, result) => test.isFalse(err)); const expectSecondConnLoggedIn = expect((err, result) => { @@ -914,78 +876,6 @@ if (Meteor.isClient) (() => { ); }, logoutStep, - - // The tests below this point are for the deprecated - // `logoutOtherClients` method. - - function (test, expect) { - // Test that Meteor.logoutOtherClients logs out a second authenticated - // connection while leaving Accounts.connection logged in. - let token; - this.secondConn = DDP.connect(Meteor.absoluteUrl()); - - const expectLoginError = expect(err => test.isTrue(err)); - const expectValidToken = expect((err, result) => { - test.isFalse(err); - test.isTrue(result); - this.tokenFromLogoutOthers = result.token; - }); - const expectSecondConnLoggedIn = expect((err, result) => { - test.equal(result.token, token); - test.isFalse(err); - // This test will fail if an unrelated reconnect triggers before the - // connection is logged out. In general our tests aren't resilient to - // mid-test reconnects. - this.secondConn.onReconnect = () => { - this.secondConn.call("login", { resume: token }, expectLoginError); - }; - Accounts.connection.call("logoutOtherClients", expectValidToken); - }); - - Meteor.loginWithPassword(this.username, this.password, expect(err => { - test.isFalse(err); - token = Accounts._storedLoginToken(); - this.beforeLogoutOthersToken = token; - test.isTrue(token); - this.secondConn.call("login", { resume: token }, - expectSecondConnLoggedIn); - })); - }, - // Test that logoutOtherClients logged out Accounts.connection and that the - // previous token is no longer valid. - waitForLoggedOutStep, - function (test, expect) { - const token = Accounts._storedLoginToken(); - test.isFalse(token); - this.secondConn.close(); - Meteor.loginWithToken( - this.beforeLogoutOthersToken, - expect(err => { - test.isTrue(err); - test.isFalse(Meteor.userId()); - }) - ); - }, - // Test that logoutOtherClients returned a new token that we can use to - // log in. - function (test, expect) { - Meteor.loginWithToken( - this.tokenFromLogoutOthers, - expect(err => { - test.isFalse(err); - test.isTrue(Meteor.userId()); - }) - ); - }, - logoutStep, - function (test, expect) { - // Test that deleting a user logs out that user's connections. - Meteor.loginWithPassword(this.username, this.password, expect(err => { - test.isFalse(err); - Accounts.connection.call("removeUser", this.username); - })); - }, - waitForLoggedOutStep ]); testAsyncMulti("passwords - validateLoginAttempt", [ @@ -1208,99 +1098,6 @@ if (Meteor.isClient) (() => { expect(() => ({}))(); } ]); - - testAsyncMulti("passwords - srp to bcrypt upgrade", [ - logoutStep, - // Create user with old SRP credentials in the database. - function (test, expect) { - Meteor.call("testCreateSRPUser", expect((error, result) => { - test.isFalse(error); - this.username = result; - })); - }, - // We are able to login with the old style credentials in the database. - function (test, expect) { - Meteor.loginWithPassword( - this.username, - 'abcdef', - expect(error => test.isFalse(error)) - ); - }, - function (test, expect) { - Meteor.call( - "testSRPUpgrade", - this.username, - expect(error => test.isFalse(error)) - ); - }, - logoutStep, - // After the upgrade to bcrypt we're still able to login. - function (test, expect) { - Meteor.loginWithPassword( - this.username, - 'abcdef', - expect(error => test.isFalse(error)) - ); - }, - logoutStep, - function (test, expect) { - Meteor.call( - "removeUser", - this.username, - expect(error => test.isFalse(error)) - ); - } - ]); - - testAsyncMulti("passwords - srp to bcrypt upgrade via password change", [ - logoutStep, - // Create user with old SRP credentials in the database. - function (test, expect) { - Meteor.call("testCreateSRPUser", expect((error, result) => { - test.isFalse(error); - this.username = result; - })); - }, - // Log in with the plaintext password handler, which should NOT upgrade us to bcrypt. - function (test, expect) { - Accounts.callLoginMethod({ - methodName: "login", - methodArguments: [ { user: { username: this.username }, password: "abcdef" } ], - userCallback: expect(err => test.isFalse(err)) - }); - }, - function (test, expect) { - Meteor.call( - "testNoSRPUpgrade", - this.username, - expect((error) => test.isFalse(error)) - ); - }, - // Changing our password should upgrade us to bcrypt. - function (test, expect) { - Accounts.changePassword( - "abcdef", - "abcdefg", - expect(error => test.isFalse(error)) - ); - }, - function (test, expect) { - Meteor.call( - "testSRPUpgrade", - this.username, - expect(error => test.isFalse(error)) - ); - }, - // And after the upgrade we should be able to change our password again. - function (test, expect) { - Accounts.changePassword( - "abcdefg", - "abcdef", - expect(error => test.isFalse(error)) - ); - }, - logoutStep - ]); }) (); @@ -1395,7 +1192,7 @@ if (Meteor.isServer) (() => { const username = Random.id(); Accounts.createUser({ username: username, - password: 'password' + password: hashPassword('password') }); makeTestConnection( @@ -1407,7 +1204,7 @@ if (Meteor.isServer) (() => { }); const result = clientConn.call('login', { user: {username: username}, - password: 'password' + password: hashPassword('password') }); test.isTrue(result); const token = Accounts._getAccountData(serverConn.id, 'loginToken'); @@ -1445,7 +1242,7 @@ if (Meteor.isServer) (() => { const userId = Accounts.createUser({ username: username, email: email, - password: "old-password" + password: hashPassword("old-password") }); const user = Meteor.users.findOne(userId); @@ -1464,15 +1261,15 @@ if (Meteor.isServer) (() => { Meteor.users.update(userId, {$set: {"emails.0.address": newEmail}}); test.throws( - () => Meteor.call("resetPassword", resetPasswordToken, "new-password"), + () => Meteor.call("resetPassword", resetPasswordToken, hashPassword("new-password")), /Token has invalid email address/ ); test.throws( () => Meteor.call( - "login", - {user: {username: username}, - password: "new-password"} - ), + "login", + {user: {username: username}, + password: hashPassword("new-password")} + ), /Incorrect password/); }); @@ -1485,7 +1282,7 @@ if (Meteor.isServer) (() => { const userId = Accounts.createUser({ username: username, email: email, - password: "old-password" + password: hashPassword("old-password") }); const user = Meteor.users.findOne(userId); @@ -1506,12 +1303,12 @@ if (Meteor.isServer) (() => { test.isTrue(clientConn.call( "resetPassword", resetPasswordToken, - "new-password" + hashPassword("new-password") )); test.isTrue(clientConn.call("login", { user: { username }, - password: "new-password" + password: hashPassword("new-password") })); onComplete(); @@ -1528,7 +1325,7 @@ if (Meteor.isServer) (() => { const userId = Accounts.createUser({ username: username, email: email, - password: "old-password" + password: hashPassword("old-password") }); const user = Meteor.users.findOne(userId); @@ -1546,14 +1343,14 @@ if (Meteor.isServer) (() => { Meteor.users.update(userId, {$set: {"services.password.reset.when": new Date(Date.now() + -5 * 24 * 3600 * 1000) }}); test.throws( - () => Meteor.call("resetPassword", resetPasswordToken, "new-password"), + () => Meteor.call("resetPassword", resetPasswordToken, hashPassword("new-password")), /Token expired/ ); test.throws( () => Meteor.call( "login", {user: {username: username}, - password: "new-password"} + password: hashPassword("new-password")} ), /Incorrect password/); }); @@ -1562,7 +1359,7 @@ if (Meteor.isServer) (() => { 'passwords - reset tokens with reasons get cleaned up', test => { const email = `${test.id}-intercept@example.com`; - const userId = Accounts.createUser({email: email, password: 'password'}); + const userId = Accounts.createUser({email: email, password: hashPassword('password')}); Accounts.sendResetPasswordEmail(userId, email); test.isTrue(!!Meteor.users.findOne(userId).services.password.reset); @@ -1575,7 +1372,7 @@ if (Meteor.isServer) (() => { 'passwords - reset tokens without reasons get cleaned up', test => { const email = `${test.id}-intercept@example.com`; - const userId = Accounts.createUser({email: email, password: 'password'}); + const userId = Accounts.createUser({email: email, password: hashPassword('password')}); Accounts.sendResetPasswordEmail(userId, email); Meteor.users.update({_id: userId}, {$unset: {"services.password.reset.reason": 1}}); test.isTrue(!!Meteor.users.findOne(userId).services.password.reset); @@ -1615,12 +1412,12 @@ if (Meteor.isServer) (() => { test.isTrue(clientConn.call( "resetPassword", enrollPasswordToken, - "new-password" + hashPassword("new-password") )); test.isTrue(clientConn.call("login", { user: { username }, - password: "new-password" + password: hashPassword("new-password") })); onComplete(); @@ -1650,37 +1447,36 @@ if (Meteor.isServer) (() => { test.isTrue(match); const enrollPasswordToken = match[1]; - Meteor.users.update(userId, {$set: {"services.password.reset.when": new Date(Date.now() + -35 * 24 * 3600 * 1000) }}); + Meteor.users.update(userId, {$set: {"services.password.enroll.when": new Date(Date.now() + -35 * 24 * 3600 * 1000) }}); test.throws( - () => Meteor.call("resetPassword", enrollPasswordToken, "new-password"), + () => Meteor.call("resetPassword", enrollPasswordToken, hashPassword("new-password")), /Token expired/ ); }); Tinytest.add('passwords - enroll tokens get cleaned up', test => { const email = `${test.id}-intercept@example.com`; - const userId = Accounts.createUser({email: email, password: 'password'}); + const userId = Accounts.createUser({email: email, password: hashPassword('password')}); Accounts.sendEnrollmentEmail(userId, email); - test.isTrue(!!Meteor.users.findOne(userId).services.password.reset); - + test.isTrue(!!Meteor.users.findOne(userId).services.password.enroll); Accounts._expirePasswordEnrollTokens(new Date(), userId); - test.isUndefined(Meteor.users.findOne(userId).services.password.reset); + test.isUndefined(Meteor.users.findOne(userId).services.password.enroll); }); Tinytest.add( "passwords - enroll tokens don't get cleaned up when reset tokens are cleaned up", test => { const email = `${test.id}-intercept@example.com`; - const userId = Accounts.createUser({email: email, password: 'password'}); + const userId = Accounts.createUser({email: email, password: hashPassword('password')}); Accounts.sendEnrollmentEmail(userId, email); - const enrollToken = Meteor.users.findOne(userId).services.password.reset; + const enrollToken = Meteor.users.findOne(userId).services.password.enroll; test.isTrue(enrollToken); Accounts._expirePasswordResetTokens(new Date(), userId); - test.equal(enrollToken, Meteor.users.findOne(userId).services.password.reset); + test.equal(enrollToken, Meteor.users.findOne(userId).services.password.enroll); } ) @@ -1688,7 +1484,7 @@ if (Meteor.isServer) (() => { "passwords - reset tokens don't get cleaned up when enroll tokens are cleaned up", test => { const email = `${test.id}-intercept@example.com`; - const userId = Accounts.createUser({email: email, password: 'password'}); + const userId = Accounts.createUser({email: email, password: hashPassword('password')}); Accounts.sendResetPasswordEmail(userId, email); const resetToken = Meteor.users.findOne(userId).services.password.reset; @@ -1923,14 +1719,14 @@ if (Meteor.isServer) (() => { Tinytest.addAsync( 'passwords - allow custom bcrypt rounds', (test, done) => { - const getUserHashRounds = user => + const getUserHashRounds = user => Number(user.services.password.bcrypt.substring(4, 6)); - + // Verify that a bcrypt hash generated for a new account uses the // default number of rounds. let username = Random.id(); - const password = 'abc123'; + const password = hashPassword('abc123'); const userId1 = Accounts.createUser({ username, password }); let user1 = Meteor.users.findOne(userId1); let rounds = getUserHashRounds(user1); diff --git a/packages/accounts-password/password_tests_setup.js b/packages/accounts-password/password_tests_setup.js index 9b34358c21..82aba8c893 100644 --- a/packages/accounts-password/password_tests_setup.js +++ b/packages/accounts-password/password_tests_setup.js @@ -132,39 +132,3 @@ Meteor.methods({ }, removeUser: username => Meteor.users.remove({ "username": username }), }); - - -// Create a user that had previously logged in with SRP. - -Meteor.methods({ - testCreateSRPUser: () => { - const username = Random.id(); - Meteor.users.remove({username: username}); - const userId = Accounts.createUser({username: username}); - Meteor.users.update( - userId, - { '$set': { 'services.password.srp': { - "identity" : "iPNrshUEcpOSO5fRDu7o4RRDc9OJBCGGljYpcXCuyg9", - "salt" : "Dk3lFggdEtcHU3aKm6Odx7sdcaIrMskQxBbqtBtFzt6", - "verifier" : "2e8bce266b1357edf6952cc56d979db19f699ced97edfb2854b95972f820b0c7006c1a18e98aad40edf3fe111b87c52ef7dd06b320ce452d01376df2d560fdc4d8e74f7a97bca1f67b3cfaef34dee34dd6c76571c247d762624dc166dab5499da06bc9358528efa75bf74e2e7f5a80d09e60acf8856069ae5cfb080f2239ee76" - } } } - ); - return username; - }, - - testSRPUpgrade: username => { - const user = Meteor.users.findOne({username: username}); - if (user.services && user.services.password && user.services.password.srp) - throw new Error("srp wasn't removed"); - if (!(user.services && user.services.password && user.services.password.bcrypt)) - throw new Error("bcrypt wasn't added"); - }, - - testNoSRPUpgrade: username => { - const user = Meteor.users.findOne({username: username}); - if (user.services && user.services.password && user.services.password.bcrypt) - throw new Error("bcrypt was added"); - if (user.services && user.services.password && ! user.services.password.srp) - throw new Error("srp was removed"); - } -}); diff --git a/packages/accounts-twitter/package.js b/packages/accounts-twitter/package.js index 196726d96b..998e50a176 100644 --- a/packages/accounts-twitter/package.js +++ b/packages/accounts-twitter/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Twitter accounts", - version: "1.4.2", + version: "1.5.0-beta230.3", }); Package.onUse(api => { diff --git a/packages/accounts-ui-unstyled/package.js b/packages/accounts-ui-unstyled/package.js index 6f5f767058..95bd3fe743 100644 --- a/packages/accounts-ui-unstyled/package.js +++ b/packages/accounts-ui-unstyled/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Unstyled version of login widgets", - version: "1.4.3" + version: "1.5.0-beta230.3" }); Package.onUse(function (api) { diff --git a/packages/accounts-ui/package.js b/packages/accounts-ui/package.js index 6cbba924cc..1c09a1dec0 100644 --- a/packages/accounts-ui/package.js +++ b/packages/accounts-ui/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Simple templates to add login widgets to an app", - version: "1.3.2", + version: "1.4.0-beta230.3", }); Package.onUse(api => { diff --git a/packages/accounts-weibo/package.js b/packages/accounts-weibo/package.js index 27b6426c18..4f921111f5 100644 --- a/packages/accounts-weibo/package.js +++ b/packages/accounts-weibo/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Sina Weibo accounts", - version: "1.3.2", + version: "1.4.0-beta230.3", }); Package.onUse(api => { diff --git a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json index 8eb0913707..42d1625ade 100644 --- a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json +++ b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json @@ -54,9 +54,9 @@ "integrity": "sha512-JIB2+XJrb7v3zceV2XzDhGIB902CmKGSpSl4q2C6agU9SNLG/2V1RtFRGPG1Ajh9STj3+q6zJMOC+N/pp2P9DA==" }, "@babel/helper-define-polyfill-provider": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz", - "integrity": "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==" + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz", + "integrity": "sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew==" }, "@babel/helper-explode-assignable-expression": { "version": "7.13.0", @@ -389,9 +389,9 @@ "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==" }, "@meteorjs/babel": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@meteorjs/babel/-/babel-7.11.0.tgz", - "integrity": "sha512-/6qog7HOFSCs42/5Svw4VbchZ4B83E7hRwjP1mDPahHqDYer3rFAipYpI6ViM/RwZSeo4MHspHUJYkENYz+JLw==" + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@meteorjs/babel/-/babel-7.11.1.tgz", + "integrity": "sha512-5WZZ3v7F2B5KpKBSOYe3kh83ijrj6o7oenCadqH59Lp9ONorZGJePQQg1OotepKzwNflBiw60cy9eFgi+/qhsw==" }, "acorn": { "version": "6.4.2", @@ -504,19 +504,19 @@ "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=" }, "babel-plugin-polyfill-corejs2": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz", - "integrity": "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", + "integrity": "sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==" }, "babel-plugin-polyfill-corejs3": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz", - "integrity": "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.2.tgz", + "integrity": "sha512-l1Cf8PKk12eEk5QP/NQ6TH8A1pee6wWDJ96WjxrMXFLHLOBFzYM4moG80HFgduVhTqAFez4alnZKEhP/bYHg0A==" }, "babel-plugin-polyfill-regenerator": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz", - "integrity": "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz", + "integrity": "sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==" }, "babel-plugin-transform-inline-consecutive-adds": { "version": "0.4.3", @@ -594,9 +594,9 @@ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==" }, "caniuse-lite": { - "version": "1.0.30001228", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz", - "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==" + "version": "1.0.30001230", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz", + "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==" }, "chalk": { "version": "2.4.2", @@ -624,9 +624,9 @@ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==" }, "core-js-compat": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.12.1.tgz", - "integrity": "sha512-i6h5qODpw6EsHAoIdQhKoZdWn+dGBF3dSS8m5tif36RlWvW3A6+yu2S16QHUo3CrkzrnEskMAt9f8FxmY9fhWQ==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.13.0.tgz", + "integrity": "sha512-jhbI2zpVskgfDC9mGRaDo1gagd0E0i/kYW0+WvibL/rafEHKAHO653hEXIxJHqRlRLITluXtRH3AGTL5qJmifQ==", "dependencies": { "semver": { "version": "7.0.0", @@ -646,9 +646,9 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==" }, "electron-to-chromium": { - "version": "1.3.734", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.734.tgz", - "integrity": "sha512-iQF2mjPZ6zNNq45kbJ6MYZYCBNdv2JpGiJC/lVx4tGJWi9MNg73KkL9sWGN4X4I/CP2SBLWsT8nPADZZpAHIyw==" + "version": "1.3.741", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.741.tgz", + "integrity": "sha512-4i3T0cwnHo1O4Mnp9JniEco8bZiXoqbm3PhW5hv7uu8YLg35iajYrRnNyKFaN8/8SSTskU2hYqVTeYVPceSpUA==" }, "escalade": { "version": "3.1.1", @@ -766,9 +766,9 @@ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==" }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "regenerate": { "version": "1.4.2", @@ -860,9 +860,9 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==" + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", + "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==" }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", diff --git a/packages/babel-compiler/package.js b/packages/babel-compiler/package.js index 3436b92bff..bae5102f6b 100644 --- a/packages/babel-compiler/package.js +++ b/packages/babel-compiler/package.js @@ -6,11 +6,11 @@ Package.describe({ // isn't possible because you can't publish a non-recommended // release with package versions that don't have a pre-release // identifier at the end (eg, -dev) - version: '7.6.2-beta230.0' + version: '7.6.2-beta230.3' }); Npm.depends({ - '@meteorjs/babel': '7.11.0', + '@meteorjs/babel': '7.11.1', 'json5': '2.1.1' }); diff --git a/packages/ddp-client/client/client_convenience.js b/packages/ddp-client/client/client_convenience.js index 4111eac4cc..4f21582a37 100644 --- a/packages/ddp-client/client/client_convenience.js +++ b/packages/ddp-client/client/client_convenience.js @@ -55,13 +55,3 @@ Meteor.connection = DDP.connect(ddpUrl, { ].forEach(name => { Meteor[name] = Meteor.connection[name].bind(Meteor.connection); }); - -// Meteor.connection used to be called -// Meteor.default_connection. Provide backcompat as a courtesy even -// though it was never documented. -// XXX COMPAT WITH 0.6.4 -Meteor.default_connection = Meteor.connection; - -// We should transition from Meteor.connect to DDP.connect. -// XXX COMPAT WITH 0.6.4 -Meteor.connect = DDP.connect; diff --git a/packages/ddp-client/common/livedata_connection.js b/packages/ddp-client/common/livedata_connection.js index f3992944c2..d812093fe5 100644 --- a/packages/ddp-client/common/livedata_connection.js +++ b/packages/ddp-client/common/livedata_connection.js @@ -160,7 +160,7 @@ export class Connection { // documents written by a given method's stub. keys are associated with // methods whose stub wrote at least one document, and whose data-done message // has not yet been received. - self._documentsWrittenByStub = Object.create(null); + self._documentsWrittenByStub = {}; // collection -> IdMap of "server document" object. A "server document" has: // - "document": the version of the document according the // server (ie, the snapshot before a stub wrote it, amended by any changes @@ -168,7 +168,7 @@ export class Connection { // It is undefined if we think the document does not exist // - "writtenByStubs": a set of method IDs whose stubs wrote to the document // whose "data done" messages have not yet been processed - self._serverDocuments = Object.create(null); + self._serverDocuments = {}; // Array of callbacks to be called after the next update of the local // cache. Used for: @@ -197,16 +197,16 @@ export class Connection { // Map from method ID -> true. Methods are removed from this when their // "data done" message is received, and we will not quiesce until it is // empty. - self._methodsBlockingQuiescence = Object.create(null); + self._methodsBlockingQuiescence = {}; // map from sub ID -> true for subs that were ready (ie, called the sub // ready callback) before reconnect but haven't become ready again yet - self._subsBeingRevived = Object.create(null); // map from sub._id -> true + self._subsBeingRevived = {}; // map from sub._id -> true // if true, the next data update should reset all stores. (set during // reconnect.) self._resetStores = false; // name -> array of updates for (yet to be created) collections - self._updatesForUnknownStores = Object.create(null); + self._updatesForUnknownStores = {}; // if we're blocking a migration, the retry func self._retryMigrate = null; @@ -216,7 +216,7 @@ export class Connection { self ); // Collection name -> array of messages. - self._bufferedWrites = Object.create(null); + self._bufferedWrites = {}; // When current buffer of updates must be flushed at, in ms timestamp. self._bufferedWritesFlushAt = null; // Timeout handle for the next processing of all pending writes @@ -236,7 +236,7 @@ export class Connection { // an error, XXX COMPAT WITH 1.0.3.1) // - stopCallback (an optional callback to call when the sub terminates // for any reason, with an error argument if an error triggered the stop) - self._subscriptions = Object.create(null); + self._subscriptions = {}; // Reactive userId. self._userId = null; @@ -1622,11 +1622,10 @@ export class Connection { } if (msg === null || !msg.msg) { - // XXX COMPAT WITH 0.6.6. ignore the old welcome message for back - // compat. Remove this 'if' once the server stops sending welcome - // messages (stream_server.js). - if (!(msg && msg.server_id)) + if(!msg || !msg.testMessageOnConnect) { + if (Object.keys(msg).length === 1 && msg.server_id) return; Meteor._debug('discarding invalid livedata message', msg); + } return; } diff --git a/packages/ddp-client/common/namespace.js b/packages/ddp-client/common/namespace.js index 1023247478..3fc0685eb6 100644 --- a/packages/ddp-client/common/namespace.js +++ b/packages/ddp-client/common/namespace.js @@ -1,6 +1,5 @@ import { DDPCommon } from 'meteor/ddp-common'; import { Meteor } from 'meteor/meteor'; -import { keys } from "meteor/ddp-common/utils.js"; import { Connection } from './livedata_connection.js'; diff --git a/packages/ddp-client/package.js b/packages/ddp-client/package.js index ec6a76bdae..727d2397dd 100644 --- a/packages/ddp-client/package.js +++ b/packages/ddp-client/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's latency-compensated distributed data client", - version: '2.4.1', + version: '2.5.0-beta230.3', documentation: null }); diff --git a/packages/ddp-rate-limiter/package.js b/packages/ddp-rate-limiter/package.js index f96ca1cf3e..b966dc3f39 100644 --- a/packages/ddp-rate-limiter/package.js +++ b/packages/ddp-rate-limiter/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'ddp-rate-limiter', - version: '1.0.9', + version: '1.1.0-beta230.3', // Brief, one-line summary of the package. summary: 'The DDPRateLimiter allows users to add rate limits to DDP' + ' methods and subscriptions.', diff --git a/packages/ddp-server/livedata_server.js b/packages/ddp-server/livedata_server.js index 6ca24210d1..99aa5a89ed 100644 --- a/packages/ddp-server/livedata_server.js +++ b/packages/ddp-server/livedata_server.js @@ -119,7 +119,7 @@ var SessionCollectionView = function (collectionName, sessionCallbacks) { DDPServer._SessionCollectionView = SessionCollectionView; -_.extend(SessionCollectionView.prototype, { +Object.assign(SessionCollectionView.prototype, { isEmpty: function () { var self = this; @@ -324,7 +324,7 @@ var Session = function (server, version, socket, options) { "livedata", "sessions", 1); }; -_.extend(Session.prototype, { +Object.assign(Session.prototype, { sendReady: function (subscriptionIds) { var self = this; @@ -1045,7 +1045,7 @@ var Subscription = function ( "livedata", "subscriptions", 1); }; -_.extend(Subscription.prototype, { +Object.assign(Subscription.prototype, { _runHandler: function () { // XXX should we unblock() here? Either before running the publish // function, or before running _publishCursor. @@ -1438,7 +1438,7 @@ Server = function (options) { }); }; -_.extend(Server.prototype, { +Object.assign(Server.prototype, { /** * @summary Register a callback to be called when a new DDP connection is made to the server. diff --git a/packages/ddp-server/package.js b/packages/ddp-server/package.js index 3a84a21baa..b75b250a6e 100644 --- a/packages/ddp-server/package.js +++ b/packages/ddp-server/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's latency-compensated distributed data server", - version: '2.4.0-beta230.0', + version: '2.4.0-beta230.3', documentation: null }); diff --git a/packages/ddp-server/server_convenience.js b/packages/ddp-server/server_convenience.js index 28fca40b2b..5d34986053 100755 --- a/packages/ddp-server/server_convenience.js +++ b/packages/ddp-server/server_convenience.js @@ -15,8 +15,3 @@ _.each(['publish', 'methods', 'call', 'apply', 'onConnection', 'onMessage'], function (name) { Meteor[name] = _.bind(Meteor.server[name], Meteor.server); }); - -// Meteor.server used to be called Meteor.default_server. Provide -// backcompat as a courtesy even though it was never documented. -// XXX COMPAT WITH 0.6.4 -Meteor.default_server = Meteor.server; diff --git a/packages/ddp-server/stream_server.js b/packages/ddp-server/stream_server.js index cf044a1d95..b39373f2f9 100644 --- a/packages/ddp-server/stream_server.js +++ b/packages/ddp-server/stream_server.js @@ -1,5 +1,3 @@ -var url = Npm.require('url'); - // By default, we use the permessage-deflate extension with default // configuration. If $SERVER_WEBSOCKET_COMPRESSION is set, then it must be valid // JSON. If it represents a falsey value, then we do not use permessage-deflate @@ -119,12 +117,11 @@ StreamServer = function () { }); self.open_sockets.push(socket); - // XXX COMPAT WITH 0.6.6. Send the old style welcome message, which - // will force old clients to reload. Remove this once we're not - // concerned about people upgrading from a pre-0.7.0 release. Also, - // remove the clause in the client that ignores the welcome message - // (livedata_connection.js) - socket.send(JSON.stringify({server_id: "0"})); + // only to send a message after connection on tests, useful for + // socket-stream-client/server-tests.js + if (process.env.TEST_METADATA) { + socket.send(JSON.stringify({ testMessageOnConnect: true })); + } // call all our callbacks when we get a new socket. they will do the // work of setting up handlers and such for specific messages. @@ -135,7 +132,7 @@ StreamServer = function () { }; -_.extend(StreamServer.prototype, { +Object.assign(StreamServer.prototype, { // call my callback when a new socket connects. // also call it for all current connections. register: function (callback) { @@ -161,7 +158,7 @@ _.extend(StreamServer.prototype, { // (meaning prior to any connect middlewares) so we need to take // an approach similar to overshadowListeners in // https://github.com/sockjs/sockjs-node/blob/cf820c55af6a9953e16558555a31decea554f70e/src/utils.coffee - _.each(['request', 'upgrade'], function(event) { + ['request', 'upgrade'].forEach((event) => { var httpServer = WebApp.httpServer; var oldHttpServerListeners = httpServer.listeners(event).slice(0); httpServer.removeAllListeners(event); @@ -172,6 +169,9 @@ _.extend(StreamServer.prototype, { // Store arguments for use within the closure below var args = arguments; + // TODO replace with url package + var url = Npm.require('url'); + // Rewrite /websocket and /websocket/ urls to /sockjs/websocket while // preserving query string. var parsedUrl = url.parse(request.url); diff --git a/packages/deprecated/amplify/package.js b/packages/deprecated/amplify/package.js index 66c3652c9b..d64a60e327 100644 --- a/packages/deprecated/amplify/package.js +++ b/packages/deprecated/amplify/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "API for Persistent Storage, PubSub and Request", - version: "1.0.0" + version: "1.0.0", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/backbone/package.js b/packages/deprecated/backbone/package.js index 6d4d29996a..ffecaadd41 100644 --- a/packages/deprecated/backbone/package.js +++ b/packages/deprecated/backbone/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "A minimalist client-side MVC framework", - version: "1.0.0" + version: "1.0.0", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/bootstrap/package.js b/packages/deprecated/bootstrap/package.js index 1098a917ed..c775a689f3 100644 --- a/packages/deprecated/bootstrap/package.js +++ b/packages/deprecated/bootstrap/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Front-end framework from Twitter", - version: "1.0.1" + version: "1.0.1", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/code-prettify/package.js b/packages/deprecated/code-prettify/package.js index 4be4dedee1..a1ea2ca33d 100644 --- a/packages/deprecated/code-prettify/package.js +++ b/packages/deprecated/code-prettify/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: '(Deprecated) Syntax highlighting of code, from Google', - version: '2.0.0' + version: '2.0.0', + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/d3/package.js b/packages/deprecated/d3/package.js index f83c2dbf2a..bc0c8ee69c 100644 --- a/packages/deprecated/d3/package.js +++ b/packages/deprecated/d3/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Library for manipulating documents based on data", - version: "1.0.0" + version: "1.0.0", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deps/.gitignore b/packages/deprecated/deps/.gitignore similarity index 100% rename from packages/deps/.gitignore rename to packages/deprecated/deps/.gitignore diff --git a/packages/deps/README.md b/packages/deprecated/deps/README.md similarity index 100% rename from packages/deps/README.md rename to packages/deprecated/deps/README.md diff --git a/packages/deps/package.js b/packages/deprecated/deps/package.js similarity index 85% rename from packages/deps/package.js rename to packages/deprecated/deps/package.js index 7a3e107d77..b4ea1af666 100644 --- a/packages/deps/package.js +++ b/packages/deprecated/deps/package.js @@ -2,7 +2,8 @@ Package.describe({ summary: "Deprecated: Use the 'tracker' package instead.", - version: '1.0.12' + version: '1.0.12', + deprecated: 'Use the \'tracker\' package instead.' }); Package.onUse(function (api) { diff --git a/packages/deprecated/facebook/package.js b/packages/deprecated/facebook/package.js index 6eeda137a4..503a9803bf 100644 --- a/packages/deprecated/facebook/package.js +++ b/packages/deprecated/facebook/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "DEPRECATED - Use facebook-oauth instead - Facebook OAuth flow", - version: "1.3.0" + version: "1.3.0", + deprecated: 'Use facebook-oauth instead' }); Package.onUse(function(api) { diff --git a/packages/deprecated/facts/package.js b/packages/deprecated/facts/package.js index 1c73053180..5d2ffe4148 100644 --- a/packages/deprecated/facts/package.js +++ b/packages/deprecated/facts/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Publish internal app statistics", - version: '1.0.9' + version: '1.0.9', + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/fastclick/package.js b/packages/deprecated/fastclick/package.js index bef7ca2e6e..8d8934ae7d 100755 --- a/packages/deprecated/fastclick/package.js +++ b/packages/deprecated/fastclick/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Faster touch events on mobile", - version: '1.0.13' + version: '1.0.13', + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/github/package.js b/packages/deprecated/github/package.js index a87308e0c3..f88f75fc86 100644 --- a/packages/deprecated/github/package.js +++ b/packages/deprecated/github/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: 'DEPRECATED - Use github-oauth instead - GitHub OAuth flow', - version: '1.2.0' + version: '1.2.0', + deprecated: 'Use github-oauth instead' }); Package.onUse(function (api) { diff --git a/packages/deprecated/google/package.js b/packages/deprecated/google/package.js index 50c9b1ec6d..bddd812c8e 100644 --- a/packages/deprecated/google/package.js +++ b/packages/deprecated/google/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "DEPRECATED - Use google-oauth instead - Google OAuth flow", - version: "1.2.0" + version: "1.2.0", + deprecated: 'Use google-oauth instead' }); Package.onUse(function(api) { diff --git a/packages/deprecated/handlebars/package.js b/packages/deprecated/handlebars/package.js index e0fa978efe..b2a9d9fc15 100644 --- a/packages/deprecated/handlebars/package.js +++ b/packages/deprecated/handlebars/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Deprecated", - version: '1.0.7' + version: '1.0.7', + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/http/package.js b/packages/deprecated/http/package.js index a1e4035474..7f7dd4a0ba 100644 --- a/packages/deprecated/http/package.js +++ b/packages/deprecated/http/package.js @@ -1,7 +1,7 @@ Package.describe({ summary: "Make HTTP calls to remote servers", version: '1.4.4', - deprecated: 'Please use fetch' + deprecated: 'Migrate to fetch when possible' }); Npm.depends({ diff --git a/packages/deprecated/jquery-history/package.js b/packages/deprecated/jquery-history/package.js index 96a8856d20..102955a293 100644 --- a/packages/deprecated/jquery-history/package.js +++ b/packages/deprecated/jquery-history/package.js @@ -1,7 +1,8 @@ Package.describe({ name: "jquery-history", summary: "Deprecated package for HTML5 pushState", - version: "1.0.2" + version: "1.0.2", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/jquery-layout/package.js b/packages/deprecated/jquery-layout/package.js index f59be56f70..dab704e9c4 100644 --- a/packages/deprecated/jquery-layout/package.js +++ b/packages/deprecated/jquery-layout/package.js @@ -4,7 +4,8 @@ Package.describe({ name: "jquery-layout", summary: "Deprecated package for JS layout", - version: "1.0.3" + version: "1.0.3", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/jquery-waypoints/package.js b/packages/deprecated/jquery-waypoints/package.js index a1efbf02ec..2f74482cbc 100644 --- a/packages/deprecated/jquery-waypoints/package.js +++ b/packages/deprecated/jquery-waypoints/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Run a function when the user scrolls past an element", - version: "1.0.9" + version: "1.0.9", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/jsparse/package.js b/packages/deprecated/jsparse/package.js index 7e01fea896..b01b35383d 100644 --- a/packages/deprecated/jsparse/package.js +++ b/packages/deprecated/jsparse/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "(Deprecated) Full-featured JavaScript parser", - version: "2.0.0" + version: "2.0.0", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/livedata/.gitignore b/packages/deprecated/livedata/.gitignore similarity index 100% rename from packages/livedata/.gitignore rename to packages/deprecated/livedata/.gitignore diff --git a/packages/livedata/DDP.md b/packages/deprecated/livedata/DDP.md similarity index 100% rename from packages/livedata/DDP.md rename to packages/deprecated/livedata/DDP.md diff --git a/packages/livedata/README.md b/packages/deprecated/livedata/README.md similarity index 100% rename from packages/livedata/README.md rename to packages/deprecated/livedata/README.md diff --git a/packages/livedata/package.js b/packages/deprecated/livedata/package.js similarity index 91% rename from packages/livedata/package.js rename to packages/deprecated/livedata/package.js index 1a5ad0a82e..572ce29662 100644 --- a/packages/livedata/package.js +++ b/packages/deprecated/livedata/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Moved to the 'ddp' package", - version: '1.0.18' + version: '1.0.18', + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/markdown/README.md b/packages/deprecated/markdown/README.md index 9353449870..4cd4a46501 100644 --- a/packages/deprecated/markdown/README.md +++ b/packages/deprecated/markdown/README.md @@ -1,5 +1,7 @@ # markdown -[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/markdown) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/markdown) +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/deprecated/markdown) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/deprecated/markdown) *** -This is an internal Meteor package. \ No newline at end of file +This is an internal Meteor package. + +[How to use it at Meteor Docs](https://docs.meteor.com/packages/markdown.html) diff --git a/packages/deprecated/markdown/package.js b/packages/deprecated/markdown/package.js index acae2f89d3..c6959442af 100644 --- a/packages/deprecated/markdown/package.js +++ b/packages/deprecated/markdown/package.js @@ -2,15 +2,14 @@ Package.describe({ summary: "Markdown-to-HTML processor", - version: "1.0.14" + version: "2.0.0-beta230.3", + deprecated: true }); Package.onUse(function (api) { - api.addFiles("showdown.js"); - api.export('Showdown'); - - api.use("templating@1.3.1", "client", {weak: true}); - api.addFiles('template-integration.js', 'client'); + api.use('ecmascript'); + api.use("templating@1.4.0", "client", {weak: true}); + api.mainModule('template-integration.js', 'client', { lazy: true }); }); Package.onTest(function (api) { diff --git a/packages/deprecated/markdown/showdown.js b/packages/deprecated/markdown/showdown.js index f86bb4dafd..eab66e57c2 100644 --- a/packages/deprecated/markdown/showdown.js +++ b/packages/deprecated/markdown/showdown.js @@ -65,7 +65,7 @@ // Showdown namespace // // METEOR CHANGE: remove "var" so that this isn't file-local. -Showdown = { extensions: {} }; +export const Showdown = { extensions: {} }; // // forEach @@ -121,7 +121,7 @@ var g_output_modifiers = []; if (typeof module !== 'undefined' && typeof exports !== 'undefined' && typeof require !== 'undefined') { var fs = require('fs'); - if (fs) { + if (fs && fs.readdirSync) { // Search extensions folder var extensions = fs.readdirSync((__dirname || '.')+'/extensions').filter(function(file){ return ~file.indexOf('.js'); diff --git a/packages/deprecated/markdown/template-integration.js b/packages/deprecated/markdown/template-integration.js index ca65070647..ac18f486b5 100644 --- a/packages/deprecated/markdown/template-integration.js +++ b/packages/deprecated/markdown/template-integration.js @@ -1,3 +1,5 @@ +import { Showdown } from './showdown'; + if (Package.templating) { var Template = Package.templating.Template; var Blaze = Package.blaze.Blaze; // implied by `templating` diff --git a/packages/deprecated/meetup/package.js b/packages/deprecated/meetup/package.js index bcaddfedd7..100322b1f9 100644 --- a/packages/deprecated/meetup/package.js +++ b/packages/deprecated/meetup/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: 'DEPRECATED - Use meetup-oauth instead - Meetup OAuth flow', - version: '1.7.0' + version: '1.7.0', + deprecated: 'Use meetup-oauth instead' }); Package.onUse(function (api) { diff --git a/packages/deprecated/meteor-developer/package.js b/packages/deprecated/meteor-developer/package.js index 77ccbba67d..4f36151c5d 100644 --- a/packages/deprecated/meteor-developer/package.js +++ b/packages/deprecated/meteor-developer/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: 'DEPRECATED - Use meteor-developer-oauth instead - Meteor developer accounts OAuth flow', - version: '1.2.0' + version: '1.2.0', + deprecated: 'Use meteor-developer-oauth instead' }); Package.onUse(function (api) { diff --git a/packages/deprecated/meteor-platform/package.js b/packages/deprecated/meteor-platform/package.js index 6af799157f..7329bbea3f 100644 --- a/packages/deprecated/meteor-platform/package.js +++ b/packages/deprecated/meteor-platform/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "(Deprecated) Include a standard set of Meteor packages in your app", - version: '1.2.6' + version: '1.2.6', + deprecated: true }); Package.onUse(function(api) { diff --git a/packages/deprecated/meyerweb-reset/package.js b/packages/deprecated/meyerweb-reset/package.js index dec2e88d92..c0abed8172 100644 --- a/packages/deprecated/meyerweb-reset/package.js +++ b/packages/deprecated/meyerweb-reset/package.js @@ -3,7 +3,8 @@ // encourage this pattern. Maybe another solution would be better. Package.describe({ summary: "(Deprecated) reset.css v2.0 from http://meyerweb.com/eric/tools/css/reset/", - version: "2.0.0" + version: "2.0.0", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/preserve-inputs/package.js b/packages/deprecated/preserve-inputs/package.js index aade8591ae..9124e4644e 100644 --- a/packages/deprecated/preserve-inputs/package.js +++ b/packages/deprecated/preserve-inputs/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Deprecated package (now empty)", - version: "1.0.11" + version: "1.0.11", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/showdown/package.js b/packages/deprecated/showdown/package.js index 957b773890..e5705ec599 100644 --- a/packages/deprecated/showdown/package.js +++ b/packages/deprecated/showdown/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Moved to the 'markdown' package", - version: '1.0.8' + version: '1.0.8', + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/spiderable/package.js b/packages/deprecated/spiderable/package.js index 9bec266898..2a345d5ddb 100644 --- a/packages/deprecated/spiderable/package.js +++ b/packages/deprecated/spiderable/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Makes the application crawlable to web spiders", - version: "1.0.14-release-testing.0" + version: "1.0.14", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/srp/.gitignore b/packages/deprecated/srp/.gitignore similarity index 100% rename from packages/srp/.gitignore rename to packages/deprecated/srp/.gitignore diff --git a/packages/srp/README.md b/packages/deprecated/srp/README.md similarity index 100% rename from packages/srp/README.md rename to packages/deprecated/srp/README.md diff --git a/packages/srp/biginteger.js b/packages/deprecated/srp/biginteger.js similarity index 100% rename from packages/srp/biginteger.js rename to packages/deprecated/srp/biginteger.js diff --git a/packages/srp/package.js b/packages/deprecated/srp/package.js similarity index 94% rename from packages/srp/package.js rename to packages/deprecated/srp/package.js index cdb1e54d3b..8809bb0e36 100644 --- a/packages/srp/package.js +++ b/packages/deprecated/srp/package.js @@ -5,7 +5,8 @@ Package.describe({ summary: "Library for Secure Remote Password (SRP) exchanges", - version: "1.1.0" + version: "1.1.0", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/srp/srp.js b/packages/deprecated/srp/srp.js similarity index 100% rename from packages/srp/srp.js rename to packages/deprecated/srp/srp.js diff --git a/packages/srp/srp_tests.js b/packages/deprecated/srp/srp_tests.js similarity index 100% rename from packages/srp/srp_tests.js rename to packages/deprecated/srp/srp_tests.js diff --git a/packages/deprecated/standard-app-packages/package.js b/packages/deprecated/standard-app-packages/package.js index fb34e2d183..53efec245f 100644 --- a/packages/deprecated/standard-app-packages/package.js +++ b/packages/deprecated/standard-app-packages/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Moved to meteor-platform", - version: '1.0.9' + version: '1.0.9', + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/startup/package.js b/packages/deprecated/startup/package.js index 15f7655fc3..0290ed295f 100644 --- a/packages/deprecated/startup/package.js +++ b/packages/deprecated/startup/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "Deprecated package (now empty)", - version: "1.0.7" + version: "1.0.7", + deprecated: true }); Package.onUse(function (api) { diff --git a/packages/deprecated/stylus/package.js b/packages/deprecated/stylus/package.js index 2a7197eafb..1a42d6b7cf 100644 --- a/packages/deprecated/stylus/package.js +++ b/packages/deprecated/stylus/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: 'Expressive, dynamic, robust CSS', - version: "2.513.15" + version: "2.513.15", + deprecated: true }); Package.registerBuildPlugin({ diff --git a/packages/deprecated/twitter/package.js b/packages/deprecated/twitter/package.js index f0a7664806..e7346aee01 100644 --- a/packages/deprecated/twitter/package.js +++ b/packages/deprecated/twitter/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "DEPRECATED - Use twitter-oauth instead - Twitter OAuth flow", - version: '1.2.0' + version: '1.2.0', + deprecated: 'Use twitter-oauth instead' }); Package.onUse(function(api) { diff --git a/packages/deprecated/weibo/package.js b/packages/deprecated/weibo/package.js index 672c9b8bf7..ab956bbf19 100644 --- a/packages/deprecated/weibo/package.js +++ b/packages/deprecated/weibo/package.js @@ -1,6 +1,7 @@ Package.describe({ summary: "DEPRECATED - Use weibo-oauth instead - Weibo OAuth flow", - version: '1.2.0' + version: '1.2.0', + deprecated: 'Use weibo-oauth instead' }); Package.onUse(function(api) { diff --git a/packages/dynamic-import/package.js b/packages/dynamic-import/package.js index bf41cb50e1..00847b59bb 100644 --- a/packages/dynamic-import/package.js +++ b/packages/dynamic-import/package.js @@ -1,6 +1,6 @@ Package.describe({ name: "dynamic-import", - version: "0.7.0-beta230.0", + version: "0.7.0-beta230.3", summary: "Runtime support for Meteor 1.5 dynamic import(...) syntax", documentation: "README.md" }); diff --git a/packages/ecmascript/package.js b/packages/ecmascript/package.js index fd6f1aa539..67923f7d8d 100644 --- a/packages/ecmascript/package.js +++ b/packages/ecmascript/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'ecmascript', - version: '0.15.1', + version: '0.15.2-beta230.3', summary: 'Compiler plugin that supports ES2015+ in all .js files', documentation: 'README.md' }); diff --git a/packages/email/package.js b/packages/email/package.js index 7e8d480a4f..9be9c31941 100644 --- a/packages/email/package.js +++ b/packages/email/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Send email messages", - version: "2.1.0-beta230.0" + version: "2.1.0-beta230.3" }); Npm.depends({ diff --git a/packages/facebook-oauth/package.js b/packages/facebook-oauth/package.js index 1b74f7d5b8..c88e04602b 100644 --- a/packages/facebook-oauth/package.js +++ b/packages/facebook-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Facebook OAuth flow", - version: "1.8.0" + version: "1.9.0-beta230.3" }); Package.onUse(api => { diff --git a/packages/hot-module-replacement/package.js b/packages/hot-module-replacement/package.js index cd858058bf..bc0cf364eb 100644 --- a/packages/hot-module-replacement/package.js +++ b/packages/hot-module-replacement/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'hot-module-replacement', - version: '0.3.0-beta230.0', + version: '0.3.0-beta230.3', summary: 'Update code in development without reloading the page', documentation: 'README.md', debugOnly: true diff --git a/packages/launch-screen/package.js b/packages/launch-screen/package.js index 1085cdfca3..1fcf8db78a 100644 --- a/packages/launch-screen/package.js +++ b/packages/launch-screen/package.js @@ -6,7 +6,7 @@ Package.describe({ // between such packages and the build tool. name: 'launch-screen', summary: 'Default and customizable launch screen on mobile.', - version: '1.3.0-beta230.0' + version: '1.3.0-beta230.3' }); Cordova.depends({ diff --git a/packages/logic-solver/package.js b/packages/logic-solver/package.js index f173dd5633..a7781cdaec 100644 --- a/packages/logic-solver/package.js +++ b/packages/logic-solver/package.js @@ -1,19 +1,19 @@ Package.describe({ summary: "General satisfiability solver for logic problems", - version: '2.0.7' + version: '2.0.8-beta230.3' }); -Package.on_use(function (api) { +Package.onUse(function (api) { api.export('Logic'); api.use('underscore'); - api.add_files(['minisat.js', + api.addFiles(['minisat.js', 'minisat_wrapper.js', 'types.js', 'logic.js', 'optimize.js']); }); -Package.on_test(function (api) { +Package.onTest(function (api) { api.use(['tinytest', 'check', 'underscore']); api.use('logic-solver'); @@ -21,5 +21,5 @@ Package.on_test(function (api) { // ones like IE 8, so we have to exclude it from our automated // testing. It needs a browser released in the last year (say) so // that Emscripten-compiled code runs reasonably. - api.add_files('logic_tests.js', 'server'); + api.addFiles('logic_tests.js', 'server'); }); diff --git a/packages/meteor-base/package.js b/packages/meteor-base/package.js index a8b550e634..ab1d0b8adb 100644 --- a/packages/meteor-base/package.js +++ b/packages/meteor-base/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'meteor-base', - version: '1.4.0', + version: '1.5.0-beta230.3', // Brief, one-line summary of the package. summary: 'Packages that every Meteor app needs', // By default, Meteor will default to using README.md for documentation. @@ -20,7 +20,6 @@ Package.onUse(function(api) { // The protocol and client/server libraries that Meteor uses to send data 'ddp', - 'livedata', // XXX COMPAT WITH PACKAGES BUILT FOR 0.9.0. // This package uses the user agent of each incoming HTTP request to // decide whether to inject