From 6a9b52de094c1907f4c6bc03bb619870d5732204 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Mon, 27 Feb 2023 18:10:32 +0100 Subject: [PATCH 01/11] added lastPasswordChangeAt column to users --- ...227160000-add-lastPasswordChangeAt-to-users.js | 15 +++++++++++++++ src/app/models/user.ts | 6 ++++++ src/app/services/user.js | 2 ++ 3 files changed, 23 insertions(+) create mode 100644 migrations/20230227160000-add-lastPasswordChangeAt-to-users.js diff --git a/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js b/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js new file mode 100644 index 00000000..1a7b6973 --- /dev/null +++ b/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js @@ -0,0 +1,15 @@ +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('users', 'lastPasswordChangeAt', { + type: Sequelize.DATE, + allowNull: true, + }); + }, + + async down(queryInterface) { + await queryInterface.removeColumn('users', 'lastPasswordChangeAt'); + }, +}; diff --git a/src/app/models/user.ts b/src/app/models/user.ts index bd7cf19f..ec2e80d8 100644 --- a/src/app/models/user.ts +++ b/src/app/models/user.ts @@ -28,6 +28,7 @@ export interface UserAttributes { tempKey: string; avatar: string; emailVerified: boolean; + lastPasswordChangeAt: Date; } export type UserModel = ModelDefined; @@ -144,6 +145,11 @@ export default (database: Sequelize): UserModel => { type: DataTypes.BOOLEAN, defaultValue: false, }, + lastPasswordChangeAt: { + type: DataTypes.DATE, + defaultValue: new Date(), + allowNull: true, + }, }, { tableName: 'users', diff --git a/src/app/services/user.js b/src/app/services/user.js index f41af0bd..12afc846 100644 --- a/src/app/services/user.js +++ b/src/app/services/user.js @@ -349,6 +349,7 @@ module.exports = (Model, App) => { password: newPassword, mnemonic, hKey: newSalt, + lastPasswordChangeAt: new Date() }, { where: { username: { [Op.eq]: user.email } }, @@ -363,6 +364,7 @@ module.exports = (Model, App) => { user.hKey = newSalt; user.mnemonic = oldMnemonic; user.password = newPassword; + user.lastPasswordChangeAt = new Date(); await user.save(); const keys = await user.getKeyserver(); From 1e8591a550c782a79e701b169d07b6a8966a8123 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Mon, 27 Feb 2023 18:11:48 +0100 Subject: [PATCH 02/11] added missing migrations --- ...215131844-add-postgresql-uuid-extension.js | 21 +++++++++++++++++++ ...230215132330-add-uuid-column-to-folders.js | 14 +++++++++++++ ...134347-add-folder-uuid-column-to-shares.js | 13 ++++++++++++ .../20230215134840-make-folder-uuid-unique.js | 15 +++++++++++++ ...0215135015-add-folder-uuid-fk-to-shares.js | 21 +++++++++++++++++++ ...0230215162543-create-index-folders-uuid.js | 14 +++++++++++++ ...215172420-add-file-uuid-column-to-files.js | 15 +++++++++++++ .../20230215183130-create-index-uuid-files.js | 14 +++++++++++++ ...5184308-add-folder-uuid-column-to-files.js | 13 ++++++++++++ ...30215184843-add-folder-uuid-fk-to-files.js | 21 +++++++++++++++++++ ...8-add-parent-folder-uuid-column-folders.js | 14 +++++++++++++ ...15200756-add-file-uuid-column-to-shares.js | 13 ++++++++++++ ...230215200900-add-file-uuid-fk-to-shares.js | 21 +++++++++++++++++++ 13 files changed, 209 insertions(+) create mode 100644 migrations/20230215131844-add-postgresql-uuid-extension.js create mode 100644 migrations/20230215132330-add-uuid-column-to-folders.js create mode 100644 migrations/20230215134347-add-folder-uuid-column-to-shares.js create mode 100644 migrations/20230215134840-make-folder-uuid-unique.js create mode 100644 migrations/20230215135015-add-folder-uuid-fk-to-shares.js create mode 100644 migrations/20230215162543-create-index-folders-uuid.js create mode 100644 migrations/20230215172420-add-file-uuid-column-to-files.js create mode 100644 migrations/20230215183130-create-index-uuid-files.js create mode 100644 migrations/20230215184308-add-folder-uuid-column-to-files.js create mode 100644 migrations/20230215184843-add-folder-uuid-fk-to-files.js create mode 100644 migrations/20230215194018-add-parent-folder-uuid-column-folders.js create mode 100644 migrations/20230215200756-add-file-uuid-column-to-shares.js create mode 100644 migrations/20230215200900-add-file-uuid-fk-to-shares.js diff --git a/migrations/20230215131844-add-postgresql-uuid-extension.js b/migrations/20230215131844-add-postgresql-uuid-extension.js new file mode 100644 index 00000000..7a9b04ae --- /dev/null +++ b/migrations/20230215131844-add-postgresql-uuid-extension.js @@ -0,0 +1,21 @@ +'use strict'; + +/** + * While Postgres out-of-the-box supports storing UUID (Universally Unique Identifier) values in their native 128-bit form, + * generating UUID values requires a plug-in. In Postgres, a plug-in is known as an extension. + * + * To install an extension, call CREATE EXTENSION. To avoid re-installing, add IF NOT EXISTS. + * + * The extension we want is an open-source library built in C for working with UUIDs, OSSP uuid. + * A build of this library for Postgres is often bundled with an installation of Postgres such as the graphical installers provided + * by Enterprise DB or included by cloud providers such as Amazon RDS for PostgreSQL. + */ +module.exports = { + async up(queryInterface) { + await queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";'); + }, + + async down(queryInterface) { + await queryInterface.sequelize.query('DROP EXTENSION IF EXISTS "uuid-ossp";'); + } +}; diff --git a/migrations/20230215132330-add-uuid-column-to-folders.js b/migrations/20230215132330-add-uuid-column-to-folders.js new file mode 100644 index 00000000..8ef57229 --- /dev/null +++ b/migrations/20230215132330-add-uuid-column-to-folders.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('folders', 'uuid', { + type: Sequelize.UUID, + defaultValue: Sequelize.literal('uuid_generate_v4()') + }); + }, + + async down(queryInterface) { + await queryInterface.removeColumn('folders', 'uuid'); + } +}; diff --git a/migrations/20230215134347-add-folder-uuid-column-to-shares.js b/migrations/20230215134347-add-folder-uuid-column-to-shares.js new file mode 100644 index 00000000..ccb5d4d1 --- /dev/null +++ b/migrations/20230215134347-add-folder-uuid-column-to-shares.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('shares', 'folder_uuid', { + type: Sequelize.UUID, + }); + }, + + async down(queryInterface) { + await queryInterface.removeColumn('shares', 'folder_uuid'); + } +}; diff --git a/migrations/20230215134840-make-folder-uuid-unique.js b/migrations/20230215134840-make-folder-uuid-unique.js new file mode 100644 index 00000000..1c81da8b --- /dev/null +++ b/migrations/20230215134840-make-folder-uuid-unique.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + async up(queryInterface) { + await queryInterface.addConstraint('folders', { + type: 'UNIQUE', + fields: ['uuid'], + name: 'folders_uuid_UNIQUE', + }); + }, + + async down(queryInterface) { + await queryInterface.removeConstraint('folders', 'folders_uuid_UNIQUE'); + } +}; diff --git a/migrations/20230215135015-add-folder-uuid-fk-to-shares.js b/migrations/20230215135015-add-folder-uuid-fk-to-shares.js new file mode 100644 index 00000000..b92948be --- /dev/null +++ b/migrations/20230215135015-add-folder-uuid-fk-to-shares.js @@ -0,0 +1,21 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addConstraint('shares', { + type: 'FOREIGN KEY', + fields: ['folder_uuid'], + name: 'shares_folder_uuid_fkey', + references: { + table: 'folders', + field: 'uuid', + }, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }); + }, + + async down(queryInterface) { + await queryInterface.removeConstraint('shares', 'shares_folder_uuid_fkey'); + } +}; diff --git a/migrations/20230215162543-create-index-folders-uuid.js b/migrations/20230215162543-create-index-folders-uuid.js new file mode 100644 index 00000000..c678253c --- /dev/null +++ b/migrations/20230215162543-create-index-folders-uuid.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addIndex('folders', { + fields: ['uuid'], + name: 'uuid_index', + }); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeIndex('folders', 'uuid_index'); + } +}; diff --git a/migrations/20230215172420-add-file-uuid-column-to-files.js b/migrations/20230215172420-add-file-uuid-column-to-files.js new file mode 100644 index 00000000..a8581ae0 --- /dev/null +++ b/migrations/20230215172420-add-file-uuid-column-to-files.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('files', 'uuid', { + type: Sequelize.UUID, + unique: true, + defaultValue: Sequelize.literal('uuid_generate_v4()') + }); + }, + + async down(queryInterface) { + await queryInterface.removeColumn('files', 'uuid'); + } +}; diff --git a/migrations/20230215183130-create-index-uuid-files.js b/migrations/20230215183130-create-index-uuid-files.js new file mode 100644 index 00000000..dedc828d --- /dev/null +++ b/migrations/20230215183130-create-index-uuid-files.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + async up(queryInterface) { + await queryInterface.addIndex('files', { + fields: ['uuid'], + name: 'files_uuid_index', + }); + }, + + async down(queryInterface) { + await queryInterface.removeIndex('files', 'files_uuid_index'); + } +}; diff --git a/migrations/20230215184308-add-folder-uuid-column-to-files.js b/migrations/20230215184308-add-folder-uuid-column-to-files.js new file mode 100644 index 00000000..a1056771 --- /dev/null +++ b/migrations/20230215184308-add-folder-uuid-column-to-files.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('files', 'folder_uuid', { + type: Sequelize.UUID, + }); + }, + + async down(queryInterface) { + await queryInterface.removeColumn('files', 'folder_uuid'); + } +}; diff --git a/migrations/20230215184843-add-folder-uuid-fk-to-files.js b/migrations/20230215184843-add-folder-uuid-fk-to-files.js new file mode 100644 index 00000000..71e0603c --- /dev/null +++ b/migrations/20230215184843-add-folder-uuid-fk-to-files.js @@ -0,0 +1,21 @@ +'use strict'; + +module.exports = { + async up(queryInterface) { + await queryInterface.addConstraint('files', { + type: 'FOREIGN KEY', + fields: ['folder_uuid'], + name: 'files_folder_uuid_fkey', + references: { + table: 'folders', + field: 'uuid', + }, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }); + }, + + async down(queryInterface) { + await queryInterface.removeConstraint('files', 'files_folder_uuid_fkey'); + } +}; diff --git a/migrations/20230215194018-add-parent-folder-uuid-column-folders.js b/migrations/20230215194018-add-parent-folder-uuid-column-folders.js new file mode 100644 index 00000000..523e9df9 --- /dev/null +++ b/migrations/20230215194018-add-parent-folder-uuid-column-folders.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('folders', 'parent_uuid', { + type: Sequelize.UUID, + }); + }, + + async down(queryInterface) { + await queryInterface.removeColumn('folders', 'parent_uuid'); + } +}; + diff --git a/migrations/20230215200756-add-file-uuid-column-to-shares.js b/migrations/20230215200756-add-file-uuid-column-to-shares.js new file mode 100644 index 00000000..abafca57 --- /dev/null +++ b/migrations/20230215200756-add-file-uuid-column-to-shares.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('shares', 'file_uuid', { + type: Sequelize.UUID, + }); + }, + + async down(queryInterface) { + await queryInterface.removeColumn('shares', 'file_uuid'); + } +}; diff --git a/migrations/20230215200900-add-file-uuid-fk-to-shares.js b/migrations/20230215200900-add-file-uuid-fk-to-shares.js new file mode 100644 index 00000000..215a6940 --- /dev/null +++ b/migrations/20230215200900-add-file-uuid-fk-to-shares.js @@ -0,0 +1,21 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addConstraint('shares', { + type: 'FOREIGN KEY', + fields: ['file_uuid'], + name: 'shares_file_uuid_fkey', + references: { + table: 'files', + field: 'uuid', + }, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }); + }, + + async down(queryInterface) { + await queryInterface.removeConstraint('shares', 'shares_file_uuid_fkey'); + } +}; From 7ef36b918bc11e19662225e7796bc6e233c6a8be Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Mon, 27 Feb 2023 20:42:49 +0100 Subject: [PATCH 03/11] unauth token when lastPasswordChangedAt date is greater --- ...0230227160000-add-lastPasswordChangeAt-to-users.js | 4 ++-- src/app/models/user.ts | 4 ++-- src/app/services/user.js | 4 ++-- src/config/initializers/middleware.js | 11 ++++++++++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js b/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js index 1a7b6973..33b17dc1 100644 --- a/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js +++ b/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js @@ -3,13 +3,13 @@ /** @type {import('sequelize-cli').Migration} */ module.exports = { async up(queryInterface, Sequelize) { - await queryInterface.addColumn('users', 'lastPasswordChangeAt', { + await queryInterface.addColumn('users', 'last_password_changed_at', { type: Sequelize.DATE, allowNull: true, }); }, async down(queryInterface) { - await queryInterface.removeColumn('users', 'lastPasswordChangeAt'); + await queryInterface.removeColumn('users', 'last_password_changed_at'); }, }; diff --git a/src/app/models/user.ts b/src/app/models/user.ts index ec2e80d8..90324d06 100644 --- a/src/app/models/user.ts +++ b/src/app/models/user.ts @@ -28,7 +28,7 @@ export interface UserAttributes { tempKey: string; avatar: string; emailVerified: boolean; - lastPasswordChangeAt: Date; + lastPasswordChangedAt: Date; } export type UserModel = ModelDefined; @@ -145,7 +145,7 @@ export default (database: Sequelize): UserModel => { type: DataTypes.BOOLEAN, defaultValue: false, }, - lastPasswordChangeAt: { + lastPasswordChangedAt: { type: DataTypes.DATE, defaultValue: new Date(), allowNull: true, diff --git a/src/app/services/user.js b/src/app/services/user.js index 12afc846..55c961a7 100644 --- a/src/app/services/user.js +++ b/src/app/services/user.js @@ -349,7 +349,7 @@ module.exports = (Model, App) => { password: newPassword, mnemonic, hKey: newSalt, - lastPasswordChangeAt: new Date() + lastPasswordChangedAt: new Date() }, { where: { username: { [Op.eq]: user.email } }, @@ -364,7 +364,7 @@ module.exports = (Model, App) => { user.hKey = newSalt; user.mnemonic = oldMnemonic; user.password = newPassword; - user.lastPasswordChangeAt = new Date(); + user.lastPasswordChangedAt = new Date(); await user.save(); const keys = await user.getKeyserver(); diff --git a/src/config/initializers/middleware.js b/src/config/initializers/middleware.js index 59ab58ad..22e00101 100644 --- a/src/config/initializers/middleware.js +++ b/src/config/initializers/middleware.js @@ -162,7 +162,16 @@ module.exports = (App, Config) => { // const email = payload.email App.services.User.FindUserObjByEmail(email) - .then((user) => done(null, user)) + .then((user) => { + const userWithoutLastPasswordChangedAt = user.lastPasswordChangedAt === null; + const userWithLastPasswordChangedAtLowerThanToken = + user.lastPasswordChangedAt && Math.floor(new Date(user.lastPasswordChangedAt)) / 1000 < payload.iat; + if (userWithoutLastPasswordChangedAt || userWithLastPasswordChangedAtLowerThanToken) { + done(null, user); + } else { + done(null); + } + }) .catch((err) => { done(err); }); From 87893fe0accac3ed33cc76beabcc0ba9b8bba564 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Mon, 27 Feb 2023 20:45:04 +0100 Subject: [PATCH 04/11] changed migration name --- ...rs.js => 20230227160000-add-lastPasswordChangedAt-to-users.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename migrations/{20230227160000-add-lastPasswordChangeAt-to-users.js => 20230227160000-add-lastPasswordChangedAt-to-users.js} (100%) diff --git a/migrations/20230227160000-add-lastPasswordChangeAt-to-users.js b/migrations/20230227160000-add-lastPasswordChangedAt-to-users.js similarity index 100% rename from migrations/20230227160000-add-lastPasswordChangeAt-to-users.js rename to migrations/20230227160000-add-lastPasswordChangedAt-to-users.js From 9daa449085b565f7849c0aae85de762625e87fad Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Tue, 28 Feb 2023 16:54:23 +0100 Subject: [PATCH 05/11] changed allowNull to false and added defaultValue --- .../20230227160000-add-lastPasswordChangedAt-to-users.js | 3 ++- src/app/models/user.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/migrations/20230227160000-add-lastPasswordChangedAt-to-users.js b/migrations/20230227160000-add-lastPasswordChangedAt-to-users.js index 33b17dc1..2ea1a08a 100644 --- a/migrations/20230227160000-add-lastPasswordChangedAt-to-users.js +++ b/migrations/20230227160000-add-lastPasswordChangedAt-to-users.js @@ -5,7 +5,8 @@ module.exports = { async up(queryInterface, Sequelize) { await queryInterface.addColumn('users', 'last_password_changed_at', { type: Sequelize.DATE, - allowNull: true, + allowNull: false, + defaultValue: 0, }); }, diff --git a/src/app/models/user.ts b/src/app/models/user.ts index 90324d06..620d876d 100644 --- a/src/app/models/user.ts +++ b/src/app/models/user.ts @@ -148,7 +148,7 @@ export default (database: Sequelize): UserModel => { lastPasswordChangedAt: { type: DataTypes.DATE, defaultValue: new Date(), - allowNull: true, + allowNull: false, }, }, { From 3512b22e662267f6bffb0666bf59ef970a413d23 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Tue, 7 Mar 2023 18:32:22 +0100 Subject: [PATCH 06/11] refresh drive-web session tokens after password change --- src/app/middleware/passport.js | 42 +++++++++++++++++++++++++++++++++- src/app/routes/auth.ts | 23 +++---------------- src/app/routes/user.js | 6 +++-- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/app/middleware/passport.js b/src/app/middleware/passport.js index 31c7b9d0..9c93bd3b 100644 --- a/src/app/middleware/passport.js +++ b/src/app/middleware/passport.js @@ -4,12 +4,52 @@ const passport = require('passport'); const passportAuth = passport.authenticate('jwt', { session: false }); function Sign(data, secret, expires = false) { - const token = expires ? jwt.sign({ email: data }, secret, { expiresIn: '14d' }) : jwt.sign(data, secret); + const token = expires ? + jwt.sign({ email: data, iat: getDefaultIAT() }, secret, { expiresIn: '14d' }) : + jwt.sign(Object.assign(data, { iat: getDefaultIAT() }), secret); + return token; +} +function SignWithOlderIAT(data, secret) { + return jwt.sign({ email: data, iat: getOlderIAT() }, secret, { expiresIn: '14d' }); +} + +function SignNewToken(data, secret, expires = false) { + const token = expires ? + jwt.sign(getNewTokenPayload(data), secret, { expiresIn: '14d' }) : + jwt.sign(getNewTokenPayload(data), secret); return token; } +function getNewTokenPayload(userData) { + return { + payload: { + uuid: userData.uuid, + email: userData.email, + name: userData.name, + lastname: userData.lastname, + username: userData.username, + sharedWorkspace: true, + networkCredentials: { + user: userData.bridgeUser, + pass: userData.userId, + }, + iat: getDefaultIAT(), + }, + }; +} + +function getDefaultIAT() { + return Math.floor(Date.now() / 1000); +} + +function getOlderIAT() { + return Math.floor(Date.now() / 1000) + 60; +} + module.exports = { passportAuth, Sign, + SignNewToken, + SignWithOlderIAT, }; diff --git a/src/app/routes/auth.ts b/src/app/routes/auth.ts index 7e236f7e..c7c6a344 100644 --- a/src/app/routes/auth.ts +++ b/src/app/routes/auth.ts @@ -2,7 +2,7 @@ import { Router, Request, Response } from 'express'; import createHttpError from 'http-errors'; import speakeasy from 'speakeasy'; import { UserAttributes } from '../models/user'; -import { passportAuth, Sign } from '../middleware/passport'; +import { passportAuth, Sign, SignNewToken } from '../middleware/passport'; import Config from '../../config/config'; import { AuthorizedUser } from './types'; import { HttpError } from 'http-errors'; @@ -154,7 +154,7 @@ export class AuthController { this.service.User.UpdateAccountActivity(req.body.email); const userBucket = await this.service.User.GetUserBucket(userData); - const newToken = Sign(this.getNewTokenPayload(userData), this.config.get('secrets').JWT); + const newToken = SignNewToken(userData, this.config.get('secrets').JWT); const keyExists = await this.service.KeyServer.keysExists(userData); if (!keyExists && req.body.publicKey) { @@ -215,28 +215,11 @@ export class AuthController { async getNewToken(req: Request, res: Response) { const authRequest = req as Request & { user: UserAttributes }; - const newToken = Sign(this.getNewTokenPayload(authRequest.user), this.config.get('secrets').JWT); + const newToken = SignNewToken(authRequest.user, this.config.get('secrets').JWT); return res.status(200).json({ newToken }); } - private getNewTokenPayload(userData: any) { - return { - payload: { - uuid: userData.uuid, - email: userData.email, - name: userData.name, - lastname: userData.lastname, - username: userData.username, - sharedWorkspace: true, - networkCredentials: { - user: userData.bridgeUser, - pass: userData.userId, - }, - }, - }; - } - async areCredentialsCorrect(req: Request, res: Response) { if (!req.query.hashedPassword) throw createHttpError(400, 'Query params must contain the hashedPassword property'); diff --git a/src/app/routes/user.js b/src/app/routes/user.js index afaf7324..5d930106 100644 --- a/src/app/routes/user.js +++ b/src/app/routes/user.js @@ -1,6 +1,6 @@ const openpgp = require('openpgp'); const createHttpError = require('http-errors'); -const { passportAuth, Sign } = require('../middleware/passport'); +const { passportAuth, Sign, SignNewToken, SignWithOlderIAT } = require('../middleware/passport'); const Logger = require('../../lib/logger').default; const AnalyticsService = require('../../lib/analytics/AnalyticsService'); const { default: uploadAvatar } = require('../middleware/upload-avatar'); @@ -16,7 +16,9 @@ module.exports = (Router, Service, App) => { Service.User.UpdatePasswordMnemonic(req.user, currentPassword, newPassword, newSalt, mnemonic, privateKey) .then(() => { - res.status(200).send({}); + const token = SignWithOlderIAT(req.user.email, App.config.get('secrets').JWT); + const newToken = SignNewToken(req.user, App.config.get('secrets').JWT); + res.status(200).send({ token, newToken }); }) .catch((err) => { res.status(500).send({ error: err.message }); From 4e80ea16948595912d47cd7f13906e597ac4077a Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Tue, 17 Oct 2023 13:14:04 +0200 Subject: [PATCH 07/11] fixed code smell --- src/config/initializers/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/initializers/middleware.js b/src/config/initializers/middleware.js index e0f1851b..ea2eb808 100644 --- a/src/config/initializers/middleware.js +++ b/src/config/initializers/middleware.js @@ -167,7 +167,7 @@ module.exports = (App, Config) => { .then((user) => { const userWithoutLastPasswordChangedAt = user.lastPasswordChangedAt === null; const userWithLastPasswordChangedAtLowerThanToken = - user.lastPasswordChangedAt && Math.floor(new Date(user.lastPasswordChangedAt)) / 1000 < payload.iat; + user.lastPasswordChangedAt && Math.floor(new Date(user.lastPasswordChangedAt).getTime()) / 1000 < payload.iat; if (userWithoutLastPasswordChangedAt || userWithLastPasswordChangedAtLowerThanToken) { done(null, user); } else { From 3633951942e8a1d2963b4bd2f7fa0cd0bbbdee21 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Thu, 9 Nov 2023 12:26:34 +0100 Subject: [PATCH 08/11] fixed migrations and changed default value to null --- ...215131844-add-postgresql-uuid-extension.js | 21 ------------------- ...230215132330-add-uuid-column-to-folders.js | 14 ------------- ...134347-add-folder-uuid-column-to-shares.js | 13 ------------ .../20230215134840-make-folder-uuid-unique.js | 15 ------------- ...0215135015-add-folder-uuid-fk-to-shares.js | 21 ------------------- ...0230215162543-create-index-folders-uuid.js | 14 ------------- ...215172420-add-file-uuid-column-to-files.js | 15 ------------- .../20230215183130-create-index-uuid-files.js | 14 ------------- ...5184308-add-folder-uuid-column-to-files.js | 13 ------------ ...30215184843-add-folder-uuid-fk-to-files.js | 21 ------------------- ...8-add-parent-folder-uuid-column-folders.js | 14 ------------- ...15200756-add-file-uuid-column-to-shares.js | 13 ------------ ...230215200900-add-file-uuid-fk-to-shares.js | 21 ------------------- ...000-add-lastPasswordChangedAt-to-users.js} | 4 ++-- src/app/models/user.ts | 4 ++-- 15 files changed, 4 insertions(+), 213 deletions(-) delete mode 100644 migrations/20230215131844-add-postgresql-uuid-extension.js delete mode 100644 migrations/20230215132330-add-uuid-column-to-folders.js delete mode 100644 migrations/20230215134347-add-folder-uuid-column-to-shares.js delete mode 100644 migrations/20230215134840-make-folder-uuid-unique.js delete mode 100644 migrations/20230215135015-add-folder-uuid-fk-to-shares.js delete mode 100644 migrations/20230215162543-create-index-folders-uuid.js delete mode 100644 migrations/20230215172420-add-file-uuid-column-to-files.js delete mode 100644 migrations/20230215183130-create-index-uuid-files.js delete mode 100644 migrations/20230215184308-add-folder-uuid-column-to-files.js delete mode 100644 migrations/20230215184843-add-folder-uuid-fk-to-files.js delete mode 100644 migrations/20230215194018-add-parent-folder-uuid-column-folders.js delete mode 100644 migrations/20230215200756-add-file-uuid-column-to-shares.js delete mode 100644 migrations/20230215200900-add-file-uuid-fk-to-shares.js rename migrations/{20230227160000-add-lastPasswordChangedAt-to-users.js => 20230530160000-add-lastPasswordChangedAt-to-users.js} (87%) diff --git a/migrations/20230215131844-add-postgresql-uuid-extension.js b/migrations/20230215131844-add-postgresql-uuid-extension.js deleted file mode 100644 index 7a9b04ae..00000000 --- a/migrations/20230215131844-add-postgresql-uuid-extension.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -/** - * While Postgres out-of-the-box supports storing UUID (Universally Unique Identifier) values in their native 128-bit form, - * generating UUID values requires a plug-in. In Postgres, a plug-in is known as an extension. - * - * To install an extension, call CREATE EXTENSION. To avoid re-installing, add IF NOT EXISTS. - * - * The extension we want is an open-source library built in C for working with UUIDs, OSSP uuid. - * A build of this library for Postgres is often bundled with an installation of Postgres such as the graphical installers provided - * by Enterprise DB or included by cloud providers such as Amazon RDS for PostgreSQL. - */ -module.exports = { - async up(queryInterface) { - await queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";'); - }, - - async down(queryInterface) { - await queryInterface.sequelize.query('DROP EXTENSION IF EXISTS "uuid-ossp";'); - } -}; diff --git a/migrations/20230215132330-add-uuid-column-to-folders.js b/migrations/20230215132330-add-uuid-column-to-folders.js deleted file mode 100644 index 8ef57229..00000000 --- a/migrations/20230215132330-add-uuid-column-to-folders.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addColumn('folders', 'uuid', { - type: Sequelize.UUID, - defaultValue: Sequelize.literal('uuid_generate_v4()') - }); - }, - - async down(queryInterface) { - await queryInterface.removeColumn('folders', 'uuid'); - } -}; diff --git a/migrations/20230215134347-add-folder-uuid-column-to-shares.js b/migrations/20230215134347-add-folder-uuid-column-to-shares.js deleted file mode 100644 index ccb5d4d1..00000000 --- a/migrations/20230215134347-add-folder-uuid-column-to-shares.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addColumn('shares', 'folder_uuid', { - type: Sequelize.UUID, - }); - }, - - async down(queryInterface) { - await queryInterface.removeColumn('shares', 'folder_uuid'); - } -}; diff --git a/migrations/20230215134840-make-folder-uuid-unique.js b/migrations/20230215134840-make-folder-uuid-unique.js deleted file mode 100644 index 1c81da8b..00000000 --- a/migrations/20230215134840-make-folder-uuid-unique.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface) { - await queryInterface.addConstraint('folders', { - type: 'UNIQUE', - fields: ['uuid'], - name: 'folders_uuid_UNIQUE', - }); - }, - - async down(queryInterface) { - await queryInterface.removeConstraint('folders', 'folders_uuid_UNIQUE'); - } -}; diff --git a/migrations/20230215135015-add-folder-uuid-fk-to-shares.js b/migrations/20230215135015-add-folder-uuid-fk-to-shares.js deleted file mode 100644 index b92948be..00000000 --- a/migrations/20230215135015-add-folder-uuid-fk-to-shares.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addConstraint('shares', { - type: 'FOREIGN KEY', - fields: ['folder_uuid'], - name: 'shares_folder_uuid_fkey', - references: { - table: 'folders', - field: 'uuid', - }, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }); - }, - - async down(queryInterface) { - await queryInterface.removeConstraint('shares', 'shares_folder_uuid_fkey'); - } -}; diff --git a/migrations/20230215162543-create-index-folders-uuid.js b/migrations/20230215162543-create-index-folders-uuid.js deleted file mode 100644 index c678253c..00000000 --- a/migrations/20230215162543-create-index-folders-uuid.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addIndex('folders', { - fields: ['uuid'], - name: 'uuid_index', - }); - }, - - async down(queryInterface, Sequelize) { - await queryInterface.removeIndex('folders', 'uuid_index'); - } -}; diff --git a/migrations/20230215172420-add-file-uuid-column-to-files.js b/migrations/20230215172420-add-file-uuid-column-to-files.js deleted file mode 100644 index a8581ae0..00000000 --- a/migrations/20230215172420-add-file-uuid-column-to-files.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addColumn('files', 'uuid', { - type: Sequelize.UUID, - unique: true, - defaultValue: Sequelize.literal('uuid_generate_v4()') - }); - }, - - async down(queryInterface) { - await queryInterface.removeColumn('files', 'uuid'); - } -}; diff --git a/migrations/20230215183130-create-index-uuid-files.js b/migrations/20230215183130-create-index-uuid-files.js deleted file mode 100644 index dedc828d..00000000 --- a/migrations/20230215183130-create-index-uuid-files.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface) { - await queryInterface.addIndex('files', { - fields: ['uuid'], - name: 'files_uuid_index', - }); - }, - - async down(queryInterface) { - await queryInterface.removeIndex('files', 'files_uuid_index'); - } -}; diff --git a/migrations/20230215184308-add-folder-uuid-column-to-files.js b/migrations/20230215184308-add-folder-uuid-column-to-files.js deleted file mode 100644 index a1056771..00000000 --- a/migrations/20230215184308-add-folder-uuid-column-to-files.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addColumn('files', 'folder_uuid', { - type: Sequelize.UUID, - }); - }, - - async down(queryInterface) { - await queryInterface.removeColumn('files', 'folder_uuid'); - } -}; diff --git a/migrations/20230215184843-add-folder-uuid-fk-to-files.js b/migrations/20230215184843-add-folder-uuid-fk-to-files.js deleted file mode 100644 index 71e0603c..00000000 --- a/migrations/20230215184843-add-folder-uuid-fk-to-files.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface) { - await queryInterface.addConstraint('files', { - type: 'FOREIGN KEY', - fields: ['folder_uuid'], - name: 'files_folder_uuid_fkey', - references: { - table: 'folders', - field: 'uuid', - }, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }); - }, - - async down(queryInterface) { - await queryInterface.removeConstraint('files', 'files_folder_uuid_fkey'); - } -}; diff --git a/migrations/20230215194018-add-parent-folder-uuid-column-folders.js b/migrations/20230215194018-add-parent-folder-uuid-column-folders.js deleted file mode 100644 index 523e9df9..00000000 --- a/migrations/20230215194018-add-parent-folder-uuid-column-folders.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addColumn('folders', 'parent_uuid', { - type: Sequelize.UUID, - }); - }, - - async down(queryInterface) { - await queryInterface.removeColumn('folders', 'parent_uuid'); - } -}; - diff --git a/migrations/20230215200756-add-file-uuid-column-to-shares.js b/migrations/20230215200756-add-file-uuid-column-to-shares.js deleted file mode 100644 index abafca57..00000000 --- a/migrations/20230215200756-add-file-uuid-column-to-shares.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addColumn('shares', 'file_uuid', { - type: Sequelize.UUID, - }); - }, - - async down(queryInterface) { - await queryInterface.removeColumn('shares', 'file_uuid'); - } -}; diff --git a/migrations/20230215200900-add-file-uuid-fk-to-shares.js b/migrations/20230215200900-add-file-uuid-fk-to-shares.js deleted file mode 100644 index 215a6940..00000000 --- a/migrations/20230215200900-add-file-uuid-fk-to-shares.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.addConstraint('shares', { - type: 'FOREIGN KEY', - fields: ['file_uuid'], - name: 'shares_file_uuid_fkey', - references: { - table: 'files', - field: 'uuid', - }, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }); - }, - - async down(queryInterface) { - await queryInterface.removeConstraint('shares', 'shares_file_uuid_fkey'); - } -}; diff --git a/migrations/20230227160000-add-lastPasswordChangedAt-to-users.js b/migrations/20230530160000-add-lastPasswordChangedAt-to-users.js similarity index 87% rename from migrations/20230227160000-add-lastPasswordChangedAt-to-users.js rename to migrations/20230530160000-add-lastPasswordChangedAt-to-users.js index 2ea1a08a..6a06d6e5 100644 --- a/migrations/20230227160000-add-lastPasswordChangedAt-to-users.js +++ b/migrations/20230530160000-add-lastPasswordChangedAt-to-users.js @@ -5,8 +5,8 @@ module.exports = { async up(queryInterface, Sequelize) { await queryInterface.addColumn('users', 'last_password_changed_at', { type: Sequelize.DATE, - allowNull: false, - defaultValue: 0, + allowNull: true, + defaultValue: null, }); }, diff --git a/src/app/models/user.ts b/src/app/models/user.ts index 620d876d..4f94e1d8 100644 --- a/src/app/models/user.ts +++ b/src/app/models/user.ts @@ -147,8 +147,8 @@ export default (database: Sequelize): UserModel => { }, lastPasswordChangedAt: { type: DataTypes.DATE, - defaultValue: new Date(), - allowNull: false, + allowNull: true, + defaultValue: null, }, }, { From 61c0647922c6f7dc68e6f0656b1793dcd268a906 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Thu, 9 Nov 2023 14:40:43 +0100 Subject: [PATCH 09/11] added missing lastPasswordChangedAt property --- src/app/routes/auth.ts | 1 + src/app/routes/routes.ts | 1 + src/app/routes/storage.ts | 9 +++++++-- src/config/initializers/middleware.js | 3 ++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/routes/auth.ts b/src/app/routes/auth.ts index 8a27d359..ee636ffb 100644 --- a/src/app/routes/auth.ts +++ b/src/app/routes/auth.ts @@ -200,6 +200,7 @@ export class AuthController { backupsBucket: userData.backupsBucket, avatar: userData.avatar ? await this.service.User.getSignedAvatarUrl(userData.avatar) : null, emailVerified: userData.emailVerified, + lastPasswordChangedAt: userData.lastPasswordChangedAt, }; const userTeam = null; diff --git a/src/app/routes/routes.ts b/src/app/routes/routes.ts index 5fa1972f..582663dd 100644 --- a/src/app/routes/routes.ts +++ b/src/app/routes/routes.ts @@ -106,6 +106,7 @@ export default (router: Router, service: any, App: any): Router => { backupsBucket: userData.backupsBucket, avatar: userData.avatar ? await service.User.getSignedAvatarUrl(userData.avatar) : null, emailVerified: userData.emailVerified, + lastPasswordChangedAt: userData.lastPasswordChangedAt, }; res.status(200).json({ user, token }); diff --git a/src/app/routes/storage.ts b/src/app/routes/storage.ts index 9542e468..80848a33 100644 --- a/src/app/routes/storage.ts +++ b/src/app/routes/storage.ts @@ -16,7 +16,10 @@ import { FileAlreadyExistsError, FileWithNameAlreadyExistsError } from '../services/errors/FileWithNameAlreadyExistsError'; -import { FolderAlreadyExistsError, FolderWithNameAlreadyExistsError } from '../services/errors/FolderWithNameAlreadyExistsError'; +import { + FolderAlreadyExistsError, + FolderWithNameAlreadyExistsError +} from '../services/errors/FolderWithNameAlreadyExistsError'; import * as resourceSharingMiddlewareBuilder from '../middleware/resource-sharing.middleware'; import {validate } from 'uuid'; @@ -94,7 +97,9 @@ export class StorageController { return res.status(409).send({ error: err.message }); } this.logger.error( - `[FILE/CREATE] ERROR: ${(err as Error).message}, BODY ${JSON.stringify(file)}, STACK: ${(err as Error).stack} USER: ${behalfUser.email}`, + `[FILE/CREATE] ERROR: ${(err as Error).message}, BODY ${ + JSON.stringify(file) + }, STACK: ${(err as Error).stack} USER: ${behalfUser.email}`, ); res.status(500).send({ error: 'Internal Server Error' }); } diff --git a/src/config/initializers/middleware.js b/src/config/initializers/middleware.js index ea2eb808..3cef963d 100644 --- a/src/config/initializers/middleware.js +++ b/src/config/initializers/middleware.js @@ -167,7 +167,8 @@ module.exports = (App, Config) => { .then((user) => { const userWithoutLastPasswordChangedAt = user.lastPasswordChangedAt === null; const userWithLastPasswordChangedAtLowerThanToken = - user.lastPasswordChangedAt && Math.floor(new Date(user.lastPasswordChangedAt).getTime()) / 1000 < payload.iat; + user.lastPasswordChangedAt && + Math.floor(new Date(user.lastPasswordChangedAt).getTime()) / 1000 < payload.iat; if (userWithoutLastPasswordChangedAt || userWithLastPasswordChangedAtLowerThanToken) { done(null, user); } else { From cca37627abcc044d30729c8a0b1b8613ae75151b Mon Sep 17 00:00:00 2001 From: Sergio Gutierrez Villalba Date: Tue, 19 Dec 2023 13:23:19 +0100 Subject: [PATCH 10/11] refactor(user): update lastPasswordChangedAt migration date --- ...rs.js => 20231115141235-add-lastPasswordChangedAt-to-users.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename migrations/{20230530160000-add-lastPasswordChangedAt-to-users.js => 20231115141235-add-lastPasswordChangedAt-to-users.js} (100%) diff --git a/migrations/20230530160000-add-lastPasswordChangedAt-to-users.js b/migrations/20231115141235-add-lastPasswordChangedAt-to-users.js similarity index 100% rename from migrations/20230530160000-add-lastPasswordChangedAt-to-users.js rename to migrations/20231115141235-add-lastPasswordChangedAt-to-users.js From b2da1ee55af249c019d2478b7780c6fb66e787e9 Mon Sep 17 00:00:00 2001 From: Andres Pinto Date: Mon, 22 Jan 2024 03:13:50 -0400 Subject: [PATCH 11/11] feat: small changes to add future iat to newTokens --- src/app/middleware/passport.js | 20 ++++++++++++++------ src/app/routes/user.js | 14 +++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/app/middleware/passport.js b/src/app/middleware/passport.js index 9c93bd3b..61980246 100644 --- a/src/app/middleware/passport.js +++ b/src/app/middleware/passport.js @@ -10,8 +10,15 @@ function Sign(data, secret, expires = false) { return token; } -function SignWithOlderIAT(data, secret) { - return jwt.sign({ email: data, iat: getOlderIAT() }, secret, { expiresIn: '14d' }); +function SignWithFutureIAT(data, secret) { + return jwt.sign({ email: data, iat: getFutureIAT() }, secret, { expiresIn: '14d' }); +} + +function SignNewTokenWithFutureIAT(data, secret, expires = false) { + const futureIat = getFutureIAT(); + return expires + ? jwt.sign(getNewTokenPayload(data, futureIat), secret, { expiresIn: '14d' }) + : jwt.sign(getNewTokenPayload(data, futureIat), secret); } function SignNewToken(data, secret, expires = false) { @@ -21,7 +28,7 @@ function SignNewToken(data, secret, expires = false) { return token; } -function getNewTokenPayload(userData) { +function getNewTokenPayload(userData, customIat) { return { payload: { uuid: userData.uuid, @@ -34,8 +41,8 @@ function getNewTokenPayload(userData) { user: userData.bridgeUser, pass: userData.userId, }, - iat: getDefaultIAT(), }, + iat: customIat ?? getDefaultIAT(), }; } @@ -43,7 +50,7 @@ function getDefaultIAT() { return Math.floor(Date.now() / 1000); } -function getOlderIAT() { +function getFutureIAT() { return Math.floor(Date.now() / 1000) + 60; } @@ -51,5 +58,6 @@ module.exports = { passportAuth, Sign, SignNewToken, - SignWithOlderIAT, + SignWithFutureIAT, + SignNewTokenWithFutureIAT, }; diff --git a/src/app/routes/user.js b/src/app/routes/user.js index 5d930106..cd44c81a 100644 --- a/src/app/routes/user.js +++ b/src/app/routes/user.js @@ -1,6 +1,6 @@ const openpgp = require('openpgp'); const createHttpError = require('http-errors'); -const { passportAuth, Sign, SignNewToken, SignWithOlderIAT } = require('../middleware/passport'); +const { passportAuth, Sign, SignWithFutureIAT, SignNewTokenWithFutureIAT } = require('../middleware/passport'); const Logger = require('../../lib/logger').default; const AnalyticsService = require('../../lib/analytics/AnalyticsService'); const { default: uploadAvatar } = require('../middleware/upload-avatar'); @@ -16,8 +16,8 @@ module.exports = (Router, Service, App) => { Service.User.UpdatePasswordMnemonic(req.user, currentPassword, newPassword, newSalt, mnemonic, privateKey) .then(() => { - const token = SignWithOlderIAT(req.user.email, App.config.get('secrets').JWT); - const newToken = SignNewToken(req.user, App.config.get('secrets').JWT); + const token = SignWithFutureIAT(req.user.email, App.config.get('secrets').JWT); + const newToken = SignNewTokenWithFutureIAT(req.user, App.config.get('secrets').JWT); res.status(200).send({ token, newToken }); }) .catch((err) => { @@ -158,10 +158,10 @@ module.exports = (Router, Service, App) => { res.status(200).send({ token, user }); } catch (err) { logger.error( - 'Update user error %s: %s. STACK %s. BODY %s', - req.user.email, - err.message, - err.stack || 'NO STACK', + 'Update user error %s: %s. STACK %s. BODY %s', + req.user.email, + err.message, + err.stack || 'NO STACK', req.body ); res.status(500).send({ error: 'Internal Server error' });