diff --git a/app/models/Document.js b/app/models/Document.js index 65b5c953..9b240c0b 100644 --- a/app/models/Document.js +++ b/app/models/Document.js @@ -175,19 +175,12 @@ class Document extends BaseModel { if (this.isSaving) return this; const wasDraft = !this.publishedAt; + const isCreating = !this.id; this.isSaving = true; try { let res; - if (this.id) { - res = await client.post('/documents.update', { - id: this.id, - title: this.title, - text: this.text, - lastRevision: this.revision, - ...options, - }); - } else { + if (isCreating) { const data = { parentDocument: undefined, collection: this.collection.id, @@ -199,25 +192,36 @@ class Document extends BaseModel { data.parentDocument = this.parentDocument; } res = await client.post('/documents.create', data); - if (res && res.data) this.emit('documents.create', res.data); + } else { + res = await client.post('/documents.update', { + id: this.id, + title: this.title, + text: this.text, + lastRevision: this.revision, + ...options, + }); } runInAction('Document#save', () => { invariant(res && res.data, 'Data should be available'); this.updateData(res.data); this.hasPendingChanges = false; - }); - this.emit('documents.update', { - document: this, - collectionId: this.collection.id, - }); + if (isCreating) { + this.emit('documents.create', this); + } - if (wasDraft && this.publishedAt) { - this.emit('documents.publish', { - id: this.id, + this.emit('documents.update', { + document: this, collectionId: this.collection.id, }); - } + + if (wasDraft && this.publishedAt) { + this.emit('documents.publish', { + id: this.id, + collectionId: this.collection.id, + }); + } + }); } catch (e) { this.ui.showToast('Document failed to save'); } finally { diff --git a/app/stores/DocumentsStore.js b/app/stores/DocumentsStore.js index ca91c62c..0a33a1c3 100644 --- a/app/stores/DocumentsStore.js +++ b/app/stores/DocumentsStore.js @@ -217,7 +217,7 @@ class DocumentsStore extends BaseStore { if (res && res.data) { const duped = res.data; - this.emit('documents.create', duped); + this.emit('documents.create', new Document(duped)); this.emit('documents.publish', { id: duped.id, collectionId: duped.collection.id, @@ -255,7 +255,7 @@ class DocumentsStore extends BaseStore { this.remove(data.id); }); this.on('documents.create', (data: Document) => { - this.add(new Document(data)); + this.add(data); }); this.on('documents.duplicate', (data: Document) => { this.duplicate(data); diff --git a/flow-typed/npm/bcrypt_v1.x.x.js b/flow-typed/npm/bcrypt_v1.x.x.js deleted file mode 100644 index 88a9d327..00000000 --- a/flow-typed/npm/bcrypt_v1.x.x.js +++ /dev/null @@ -1,37 +0,0 @@ -// flow-typed signature: 96d9e6596558a201899e45822d93e38d -// flow-typed version: da30fe6876/bcrypt_v1.x.x/flow_>=v0.25.x - -declare module bcrypt { - declare function genSaltSync(rounds?: number): string; - declare function genSalt(rounds: number): Promise; - declare function genSalt(): Promise; - declare function genSalt(callback: (err: Error, salt: string) => void): void; - declare function genSalt( - rounds: number, - callback: (err: Error, salt: string) => void - ): void; - declare function hashSync(data: string, salt: string): string; - declare function hashSync(data: string, rounds: number): string; - declare function hash( - data: string, - saltOrRounds: string | number - ): Promise; - declare function hash( - data: string, - rounds: number, - callback: (err: Error, encrypted: string) => void - ): void; - declare function hash( - data: string, - salt: string, - callback: (err: Error, encrypted: string) => void - ): void; - declare function compareSync(data: string, encrypted: string): boolean; - declare function compare(data: string, encrypted: string): Promise; - declare function compare( - data: string, - encrypted: string, - callback: (err: Error, same: boolean) => void - ): void; - declare function getRounds(encrypted: string): number; -} diff --git a/package.json b/package.json index 7fe87f60..fe75f7f4 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ "babel-preset-react": "6.11.1", "babel-preset-react-hmre": "1.1.1", "babel-regenerator-runtime": "6.5.0", - "bcrypt": "1.0.3", "boundless-arrow-key-navigation": "^1.0.4", "boundless-popover": "^1.0.4", "bugsnag": "^1.7.0", @@ -108,11 +107,11 @@ "imports-loader": "0.6.5", "invariant": "^2.2.2", "isomorphic-fetch": "2.2.1", - "jszip": "3.1.5", "js-cookie": "^2.1.4", "js-search": "^1.4.2", "json-loader": "0.5.4", "jsonwebtoken": "7.0.1", + "jszip": "3.1.5", "koa": "^2.2.0", "koa-bodyparser": "4.2.0", "koa-compress": "2.0.0", diff --git a/server/migrations/20180707231201-remove-passwords.js b/server/migrations/20180707231201-remove-passwords.js new file mode 100644 index 00000000..0cd2cc84 --- /dev/null +++ b/server/migrations/20180707231201-remove-passwords.js @@ -0,0 +1,11 @@ +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('users', 'passwordDigest'); + }, + down: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('users', 'passwordDigest', { + type: Sequelize.STRING, + allowNull: true, + }); + } +} \ No newline at end of file diff --git a/server/models/User.js b/server/models/User.js index f1e92226..ec5416d6 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -1,6 +1,5 @@ // @flow import crypto from 'crypto'; -import bcrypt from 'bcrypt'; import uuid from 'uuid'; import JWT from 'jsonwebtoken'; import subMinutes from 'date-fns/sub_minutes'; @@ -8,8 +7,6 @@ import { DataTypes, sequelize, encryptedFields } from '../sequelize'; import { publicS3Endpoint, uploadToS3FromUrl } from '../utils/s3'; import { sendEmail } from '../mailer'; -const BCRYPT_COST = process.env.NODE_ENV === 'production' ? 12 : 4; - const User = sequelize.define( 'user', { @@ -22,8 +19,6 @@ const User = sequelize.define( username: { type: DataTypes.STRING }, name: DataTypes.STRING, avatarUrl: { type: DataTypes.STRING, allowNull: true }, - password: DataTypes.VIRTUAL, - passwordDigest: DataTypes.STRING, isAdmin: DataTypes.BOOLEAN, service: { type: DataTypes.STRING, allowNull: true }, serviceId: { type: DataTypes.STRING, allowNull: true, unique: true }, @@ -81,24 +76,6 @@ User.prototype.getJwtToken = function() { return JWT.sign({ id: this.id }, this.jwtSecret); }; -User.prototype.verifyPassword = function(password) { - return new Promise((resolve, reject) => { - if (!this.passwordDigest) { - resolve(false); - return; - } - - bcrypt.compare(password, this.passwordDigest, (err, ok) => { - if (err) { - reject(err); - return; - } - - resolve(ok); - }); - }); -}; - const uploadAvatar = async model => { const endpoint = publicS3Endpoint(); @@ -115,24 +92,6 @@ const setRandomJwtSecret = model => { model.jwtSecret = crypto.randomBytes(64).toString('hex'); }; -const hashPassword = model => { - if (!model.password) { - return null; - } - - return new Promise((resolve, reject) => { - bcrypt.hash(model.password, BCRYPT_COST, (err, digest) => { - if (err) { - reject(err); - return; - } - - model.passwordDigest = digest; - resolve(); - }); - }); -}; - const removeIdentifyingInfo = model => { model.email = ''; model.username = ''; @@ -156,8 +115,6 @@ const checkLastAdmin = async model => { } }; -User.beforeCreate(hashPassword); -User.beforeUpdate(hashPassword); User.beforeDestroy(checkLastAdmin); User.beforeDestroy(removeIdentifyingInfo); User.beforeSave(uploadAvatar); diff --git a/server/models/User.test.js b/server/models/User.test.js index 9bdc13b6..cce0a743 100644 --- a/server/models/User.test.js +++ b/server/models/User.test.js @@ -4,11 +4,7 @@ import { buildUser } from '../test/factories'; beforeEach(flushdb); -it('should set JWT secret and password digest', async () => { - const user = await buildUser({ password: 'test123!' }); - expect(user.passwordDigest).toBeTruthy(); +it('should set JWT secret', async () => { + const user = await buildUser(); expect(user.getJwtToken()).toBeTruthy(); - - expect(await user.verifyPassword('test123!')).toBe(true); - expect(await user.verifyPassword('badPasswd')).toBe(false); }); diff --git a/server/test/factories.js b/server/test/factories.js index 7cdcee64..65b8b828 100644 --- a/server/test/factories.js +++ b/server/test/factories.js @@ -39,7 +39,6 @@ export async function buildUser(overrides: Object = {}) { email: `user${count}@example.com`, username: `user${count}`, name: `User ${count}`, - password: 'test123!', service: 'slack', serviceId: uuid.v4(), createdAt: new Date('2018-01-01T00:00:00.000Z'), diff --git a/server/test/support.js b/server/test/support.js index 2f57376d..4f6b08fa 100644 --- a/server/test/support.js +++ b/server/test/support.js @@ -28,7 +28,6 @@ const seed = async () => { email: 'user1@example.com', username: 'user1', name: 'User 1', - password: 'test123!', teamId: team.id, service: 'slack', serviceId: 'U2399UF2P', @@ -44,7 +43,6 @@ const seed = async () => { email: 'admin@example.com', username: 'admin', name: 'Admin User', - password: 'test123!', teamId: team.id, isAdmin: true, service: 'slack', diff --git a/yarn.lock b/yarn.lock index 4db08afe..fb4b6a11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1199,13 +1199,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bcrypt@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-1.0.3.tgz#b02ddc6c0b52ea16b8d3cf375d5a32e780dab548" - dependencies: - nan "2.6.2" - node-pre-gyp "0.6.36" - beeper@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" @@ -6851,10 +6844,6 @@ mz@^2.6.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" - nan@^2.0.5: version "2.8.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" @@ -6990,7 +6979,7 @@ node-notifier@^5.1.2: shellwords "^0.1.0" which "^1.2.12" -node-pre-gyp@0.6.36, node-pre-gyp@^0.6.36: +node-pre-gyp@^0.6.36: version "0.6.36" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" dependencies: