From c5082069fc752239dedcd2f62d5e89810c014f04 Mon Sep 17 00:00:00 2001 From: Tim Leslie Date: Mon, 16 Mar 2020 09:40:30 +1100 Subject: [PATCH] Add support for conditional bcrypt/bcryptjs usage --- .changeset/tender-seahorses-attack.md | 5 ++++ docs/tutorials/add-lists.md | 2 +- packages/fields/package.json | 2 +- .../src/types/Password/Implementation.js | 13 +++++----- packages/fields/src/types/Password/README.md | 24 ++++++++++++++----- yarn.lock | 18 ++++---------- 6 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 .changeset/tender-seahorses-attack.md diff --git a/.changeset/tender-seahorses-attack.md b/.changeset/tender-seahorses-attack.md new file mode 100644 index 00000000000..d19ee834079 --- /dev/null +++ b/.changeset/tender-seahorses-attack.md @@ -0,0 +1,5 @@ +--- +'@keystonejs/fields': major +--- + +Replaced default bcrypt implementation from `bcrypt` to `bcryptjs`. You can use the new `useCompiledBcrypt` config option to the `Password` field to keep the use of the `bcrypt` package. `bcrypt` must be manually listed in your `package.json` if use set `{ useCompiledBcrypt: true }`, as it is no longer a dependency of Keystone. diff --git a/docs/tutorials/add-lists.md b/docs/tutorials/add-lists.md index 1e29329d80c..affdadd9207 100644 --- a/docs/tutorials/add-lists.md +++ b/docs/tutorials/add-lists.md @@ -106,7 +106,7 @@ keystone.createList('User', UsersSchema); -Relaunch your app and check if new the list appeared in the Admin UI. +Relaunch your app and check if new the list appeared in the Admin UI. But how can we assign a task to specific user? Let's proceed with [Defining Relationships](/docs/tutorials/relationships.md) See also: diff --git a/packages/fields/package.json b/packages/fields/package.json index 7944c813a25..92aa557f10e 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -42,7 +42,7 @@ "@keystonejs/utils": "^5.2.2", "@sindresorhus/slugify": "^0.6.0", "apollo-errors": "^1.9.0", - "bcrypt": "^3.0.6", + "bcryptjs": "^2.4.3", "cuid": "^2.1.6", "date-fns": "^1.30.1", "dumb-passwords": "^0.2.1", diff --git a/packages/fields/src/types/Password/Implementation.js b/packages/fields/src/types/Password/Implementation.js index 6e136f90c24..3c057a5d1da 100644 --- a/packages/fields/src/types/Password/Implementation.js +++ b/packages/fields/src/types/Password/Implementation.js @@ -1,15 +1,16 @@ import { Implementation } from '../../Implementation'; import { MongooseFieldAdapter } from '@keystonejs/adapter-mongoose'; import { KnexFieldAdapter } from '@keystonejs/adapter-knex'; -import bcrypt from 'bcrypt'; import dumbPasswords from 'dumb-passwords'; const bcryptHashRegex = /^\$2[aby]?\$\d{1,2}\$[.\/A-Za-z0-9]{53}$/; export class Password extends Implementation { - constructor(path, { rejectCommon, minLength, workFactor }) { + constructor(path, { rejectCommon, minLength, workFactor, useCompiledBcrypt }) { super(...arguments); + this.bcrypt = require(useCompiledBcrypt ? 'bcrypt' : 'bcryptjs'); + // Sanitise field specific config this.rejectCommon = !!rejectCommon; this.minLength = Math.max(Number.parseInt(minLength) || 8, 1); @@ -50,18 +51,18 @@ export class Password extends Implementation { // The compare() and compareSync() functions are constant-time // The compare() and generateHash() functions will return a Promise if no call back is provided compare(candidate, hash, callback) { - return bcrypt.compare(candidate, hash, callback); + return this.bcrypt.compare(candidate, hash, callback); } compareSync(candidate, hash) { - return bcrypt.compareSync(candidate, hash); + return this.bcrypt.compareSync(candidate, hash); } generateHash(plaintext, callback) { this.validateNewPassword(plaintext); - return bcrypt.hash(plaintext, this.workFactor, callback); + return this.bcrypt.hash(plaintext, this.workFactor, callback); } generateHashSync(plaintext) { this.validateNewPassword(plaintext); - return bcrypt.hashSync(plaintext, this.workFactor); + return this.bcrypt.hashSync(plaintext, this.workFactor); } extendAdminMeta(meta) { diff --git a/packages/fields/src/types/Password/README.md b/packages/fields/src/types/Password/README.md index e4d79c25738..1b47193b8a3 100644 --- a/packages/fields/src/types/Password/README.md +++ b/packages/fields/src/types/Password/README.md @@ -31,12 +31,13 @@ keystone.createList('User', { ### Config -| Option | Type | Default | Description | -| -------------- | --------- | ------- | --------------------------------------------------------------------- | -| `minLength` | `Integer` | `8` | The minimum number of characters this field will accept | -| `rejectCommon` | `Boolean` | `false` | Checks the password against a list of commonly used passwords | -| `workFactor` | `Integer` | `10` | Controls the processing time required to generate and validate hashes | -| `isRequired` | `Boolean` | `false` | Does this field require a value? | +| Option | Type | Default | Description | +| ------------------- | --------- | ------- | --------------------------------------------------------------------- | +| `minLength` | `Integer` | `8` | The minimum number of characters this field will accept | +| `rejectCommon` | `Boolean` | `false` | Checks the password against a list of commonly used passwords | +| `workFactor` | `Integer` | `10` | Controls the processing time required to generate and validate hashes | +| `isRequired` | `Boolean` | `false` | Does this field require a value? | +| `useCompiledBcrypt` | `Boolean` | `false` | Use the compiled `bcrypt` package rather than `bcryptjs` | #### `minLength` @@ -77,6 +78,17 @@ Note the `workFactor` supplied is applied by the bcrypt algorithm as an exponent As such, a work factor of 11 will cause passwords to take _twice_ as long to hash and validate as a work factor of 10. A work factor of 12 will cause passwords to take _four times_ as long as 10. Etc. +#### `useCompiledBcrypt` + +By default the [`bcryptjs`](https://www.npmjs.com/package/bcryptjs) package is used for computing and comparing hashes. +This package provides a javascript implementation of the `bcrypt` algorithm. +A compiled version of this algorithm is provided by the [`bcrypt`](https://www.npmjs.com/package/bcrypt) package. +Setting `{ userCompiledBcrypt: true }` will tell Keystone to use the compiled package. +If you use this flag you must include `bcrypt` in your package dependencies. + +The compiled package provides a ~20% performance improvement, and avoids the thread blocking of the JavaScript implementation. +This comes with the trade off that the compiled package can be challenging to work with when working in some environments (e.g. Windows) or when trying to deploy code built in one environment onto a different environment (e.g. build on OSX to deploy in a Linux based lambda process). + ### Auth Strategies The `Password` field exposes a `compare()` function. diff --git a/yarn.lock b/yarn.lock index bd98bc4a714..c8c1f52daa3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5269,13 +5269,10 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bcrypt@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.6.tgz#f607846df62d27e60d5e795612c4f67d70206eb2" - integrity sha512-taA5bCTfXe7FUjKroKky9EXpdhkVvhE5owfxfLYodbrAR1Ul3juLmIQmIQBK4L9a5BuUcE6cqmwT+Da20lF9tg== - dependencies: - nan "2.13.2" - node-pre-gyp "0.12.0" +bcryptjs@^2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= better-assert@~1.0.0: version "1.0.2" @@ -16023,11 +16020,6 @@ name-all-modules-plugin@^1.0.1: resolved "https://registry.yarnpkg.com/name-all-modules-plugin/-/name-all-modules-plugin-1.0.1.tgz#0abfb6ad835718b9fb4def0674e06657a954375c" integrity sha1-Cr+2rYNXGLn7Te8GdOBmV6lUN1w= -nan@2.13.2: - version "2.13.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" - integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== - nan@^2.12.1, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -16323,7 +16315,7 @@ node-object-hash@^2.0.0: resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-2.0.0.tgz#9971fcdb7d254f05016bd9ccf508352bee11116b" integrity sha512-VZR0zroAusy1ETZMZiGeLkdu50LGjG5U1KHZqTruqtTyQ2wfWhHG2Ow4nsUbfTFGlaREgNHcCWoM/OzEm6p+NQ== -node-pre-gyp@0.12.0, node-pre-gyp@^0.12.0: +node-pre-gyp@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==