From 6ffa1661676bc5300e72863830aedad694356ada Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Mon, 29 Jun 2020 18:26:47 -0400 Subject: [PATCH] Track activity --- package-lock.json | 92 +++++++++++++++++--------------- package.json | 3 +- src/routes/collection-presets.ts | 39 ++++++++++++-- src/routes/files.ts | 47 +++++++++++++--- src/routes/folders.ts | 49 +++++++++++++---- src/routes/items.ts | 40 ++++++++++++-- src/routes/permissions.ts | 43 ++++++++++++--- src/routes/relations.ts | 39 ++++++++++++-- src/routes/roles.ts | 39 ++++++++++++-- src/routes/users.ts | 39 ++++++++++++-- src/routes/webhooks.ts | 47 ++++++++++++---- src/services/auth.ts | 4 +- src/services/files.ts | 10 +++- src/services/items.ts | 3 +- src/services/payload.ts | 8 +-- src/services/users.ts | 4 +- 16 files changed, 401 insertions(+), 105 deletions(-) diff --git a/package-lock.json b/package-lock.json index d1d9ee8f26..b52e27d91f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -231,6 +231,14 @@ "@hapi/hoek": "^9.0.0" } }, + "@phc/format": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-0.5.0.tgz", + "integrity": "sha512-JWtZ5P1bfXU0bAtTzCpOLYHDXuxSVdtL/oqz4+xa97h8w9E5IlVN333wugXVFv8vZ1hbXObKQf1ptXmFFcMByg==", + "requires": { + "safe-buffer": "^5.1.2" + } + }, "@slynova/flydrive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@slynova/flydrive/-/flydrive-1.0.1.tgz", @@ -267,12 +275,6 @@ "integrity": "sha512-8GAYQ1jDRUQkSpHzJUqXwAkYFOxuWAOGLhIR4aPd/Y/yL12Q/9m7LsKpHKlfKdNE/362Hc9wPI1Yh6opDfxVJg==", "dev": true }, - "@types/bcrypt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-3.0.0.tgz", - "integrity": "sha512-nohgNyv+1ViVcubKBh0+XiNJ3dO8nYu///9aJ4cgSqv70gBL+94SNy/iC2NLzKPT2Zt/QavrOkBVbZRLZmw6NQ==", - "dev": true - }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -647,6 +649,48 @@ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" }, + "argon2": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.26.2.tgz", + "integrity": "sha512-Tk9I/r3KIHCIHU5x2UawKsPi+g7MByAYnUZghXztQDXRp/997P31wa4qvdvokTaFBpsu6jOZACd+2qkBGGssRA==", + "requires": { + "@phc/format": "^0.5.0", + "node-addon-api": "^2.0.0", + "node-pre-gyp": "^0.14.0" + }, + "dependencies": { + "node-addon-api": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.1.tgz", + "integrity": "sha512-2WVfwRfIr1AVn3dRq4yRc2Hn35ND+mPJH6inC6bjpYCZVrpXPB4j3T6i//OGVfqVsR1t/X/axRulDsheq4F0LQ==" + }, + "node-pre-gyp": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", + "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -882,42 +926,6 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, - "bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-jB0yCBl4W/kVHM2whjfyqnxTmOHkCX4kHEa5nYKSoGeYe8YrjTYTc87/6bwt1g8cmV0QrbhKriETg9jWtcREhg==", - "requires": { - "node-addon-api": "^3.0.0", - "node-pre-gyp": "0.15.0" - }, - "dependencies": { - "node-pre-gyp": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", - "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.3", - "needle": "^2.5.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", diff --git a/package.json b/package.json index 5a15cbd902..5c61b2cb99 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "homepage": "https://github.com/directus/api-node#readme", "devDependencies": { "@types/atob": "^2.1.2", - "@types/bcrypt": "^3.0.0", "@types/busboy": "^0.2.3", "@types/express": "^4.17.6", "@types/express-pino-logger": "^4.0.2", @@ -68,8 +67,8 @@ "@slynova/flydrive": "^1.0.1", "@slynova/flydrive-gcs": "^1.0.1", "@slynova/flydrive-s3": "^1.0.1", + "argon2": "^0.26.2", "atob": "^2.1.2", - "bcrypt": "^5.0.0", "body-parser": "^1.19.0", "busboy": "^0.3.1", "camelcase": "^6.0.0", diff --git a/src/routes/collection-presets.ts b/src/routes/collection-presets.ts index 689420fd8e..052972c67e 100644 --- a/src/routes/collection-presets.ts +++ b/src/routes/collection-presets.ts @@ -4,6 +4,7 @@ import sanitizeQuery from '../middleware/sanitize-query'; import validateQuery from '../middleware/validate-query'; import * as CollectionPresetsService from '../services/collection-presets'; import useCollection from '../middleware/use-collection'; +import * as ActivityService from '../services/activity'; const router = express.Router(); @@ -11,11 +12,21 @@ router.post( '/', useCollection('directus_collection_presets'), asyncHandler(async (req, res) => { - const records = await CollectionPresetsService.createCollectionPreset( + const record = await CollectionPresetsService.createCollectionPreset( req.body, res.locals.query ); - return res.json({ data: records }); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: 'directus_collection_presets', + item: record.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: record }); }) ); @@ -48,12 +59,22 @@ router.patch( '/:pk', useCollection('directus_collection_presets'), asyncHandler(async (req, res) => { - const records = await CollectionPresetsService.updateCollectionPreset( + const record = await CollectionPresetsService.updateCollectionPreset( req.params.pk, req.body, res.locals.query ); - return res.json({ data: records }); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: 'directus_collection_presets', + item: record.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: record }); }) ); @@ -62,6 +83,16 @@ router.delete( useCollection('directus_collection_presets'), asyncHandler(async (req, res) => { await CollectionPresetsService.deleteCollectionPreset(req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: 'directus_collection_presets', + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/routes/files.ts b/src/routes/files.ts index 84e3427f6a..b03ac0417a 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -7,12 +7,14 @@ import * as FilesService from '../services/files'; import logger from '../logger'; import { InvalidPayloadException } from '../exceptions'; import useCollection from '../middleware/use-collection'; +import * as ActivityService from '../services/activity'; const router = express.Router(); const multipartHandler = (operation: 'create' | 'update') => asyncHandler(async (req, res, next) => { const busboy = new Busboy({ headers: req.headers }); + const savedFiles: Record = []; /** * The order of the fields in multipart/form-data is important. We require that all fields @@ -53,9 +55,31 @@ const multipartHandler = (operation: 'create' | 'update') => try { if (operation === 'create') { - await FilesService.createFile(payload, fileStream); + const file = await FilesService.createFile(payload, fileStream); + + ActivityService.createActivity({ + action: ActivityService.Action.UPLOAD, + collection: 'directus_files', + item: file.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + savedFiles.push(file); } else { - await FilesService.updateFile(req.params.pk, payload, fileStream); + const file = await FilesService.updateFile(req.params.pk, payload, fileStream); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: 'directus_files', + item: file.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + savedFiles.push(file); } } catch (err) { busboy.emit('error', err); @@ -67,7 +91,7 @@ const multipartHandler = (operation: 'create' | 'update') => }); busboy.on('finish', () => { - res.status(200).end(); + res.status(200).json({ data: savedFiles }); }); return req.pipe(busboy); @@ -101,13 +125,24 @@ router.patch( '/:pk', useCollection('directus_files'), asyncHandler(async (req, res, next) => { + let file: Record; + if (req.is('multipart/form-data')) { - await multipartHandler('update')(req, res, next); + file = await multipartHandler('update')(req, res, next); } else { - await FilesService.updateFile(req.params.pk, req.body); + file = await FilesService.updateFile(req.params.pk, req.body); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: 'directus_files', + item: file.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); } - return res.status(200).end(); + return res.status(200).json({ data: file }); }) ); diff --git a/src/routes/folders.ts b/src/routes/folders.ts index 732766b8bf..f61836264b 100644 --- a/src/routes/folders.ts +++ b/src/routes/folders.ts @@ -2,8 +2,10 @@ import express from 'express'; import asyncHandler from 'express-async-handler'; import sanitizeQuery from '../middleware/sanitize-query'; import validateQuery from '../middleware/validate-query'; -import * as FoldersService from '../services/folders'; import useCollection from '../middleware/use-collection'; +import * as FoldersService from '../services/folders'; +import * as ActivityService from '../services/activity'; +import * as PayloadService from '../services/payload'; const router = express.Router(); @@ -11,8 +13,19 @@ router.post( '/', useCollection('directus_folders'), asyncHandler(async (req, res) => { - const records = await FoldersService.createFolder(req.body, res.locals.query); - return res.json({ data: records }); + const payload = await PayloadService.processValues('create', req.collection, req.body); + const record = await FoldersService.createFolder(payload, res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: req.collection, + item: record.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: record }); }) ); @@ -42,12 +55,20 @@ router.patch( '/:pk', useCollection('directus_folders'), asyncHandler(async (req, res) => { - const records = await FoldersService.updateFolder( - req.params.pk, - req.body, - res.locals.query - ); - return res.json({ data: records }); + const payload = await PayloadService.processValues('create', req.collection, req.body); + + const record = await FoldersService.updateFolder(req.params.pk, payload, res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: req.collection, + item: record.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: record }); }) ); @@ -56,6 +77,16 @@ router.delete( useCollection('directus_folders'), asyncHandler(async (req, res) => { await FoldersService.deleteFolder(req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: req.collection, + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/routes/items.ts b/src/routes/items.ts index dfecda47be..6231963252 100644 --- a/src/routes/items.ts +++ b/src/routes/items.ts @@ -7,6 +7,7 @@ import validateSingleton from '../middleware/validate-singleton'; import validateQuery from '../middleware/validate-query'; import * as MetaService from '../services/meta'; import * as PayloadService from '../services/payload'; +import * as ActivityService from '../services/activity'; const router = express.Router(); @@ -16,8 +17,19 @@ router.post( validateSingleton, asyncHandler(async (req, res) => { const payload = await PayloadService.processValues('create', req.collection, req.body); - await createItem(req.params.collection, payload); - res.status(200).end(); + const item = await createItem(req.params.collection, payload); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: req.collection, + /** @TODO don't forget to use real primary key here */ + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + res.json({ data: item }); }) ); @@ -56,8 +68,18 @@ router.patch( validateCollection, asyncHandler(async (req, res) => { const payload = await PayloadService.processValues('update', req.collection, req.body); - await updateItem(req.params.collection, req.params.pk, payload); - return res.status(200).end(); + const item = await updateItem(req.params.collection, req.params.pk, payload); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -66,6 +88,16 @@ router.delete( validateCollection, asyncHandler(async (req, res) => { await deleteItem(req.params.collection, req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: req.collection, + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/routes/permissions.ts b/src/routes/permissions.ts index 121dec7834..9d2a1a7802 100644 --- a/src/routes/permissions.ts +++ b/src/routes/permissions.ts @@ -3,6 +3,7 @@ import asyncHandler from 'express-async-handler'; import sanitizeQuery from '../middleware/sanitize-query'; import validateQuery from '../middleware/validate-query'; import * as PermissionsService from '../services/permissions'; +import * as ActivityService from '../services/activity'; import useCollection from '../middleware/use-collection'; const router = express.Router(); @@ -11,8 +12,18 @@ router.post( '/', useCollection('directus_permissions'), asyncHandler(async (req, res) => { - const records = await PermissionsService.createPermission(req.body, res.locals.query); - return res.json({ data: records }); + const item = await PermissionsService.createPermission(req.body, res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -22,8 +33,8 @@ router.get( sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { - const records = await PermissionsService.readPermissions(res.locals.query); - return res.json({ data: records }); + const item = await PermissionsService.readPermissions(res.locals.query); + return res.json({ data: item }); }) ); @@ -42,12 +53,22 @@ router.patch( '/:pk', useCollection('directus_permissions'), asyncHandler(async (req, res) => { - const records = await PermissionsService.updatePermission( + const item = await PermissionsService.updatePermission( req.params.pk, req.body, res.locals.query ); - return res.json({ data: records }); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -56,6 +77,16 @@ router.delete( useCollection('directus_permissions'), asyncHandler(async (req, res) => { await PermissionsService.deletePermission(req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: req.collection, + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/routes/relations.ts b/src/routes/relations.ts index 83a1c5e2b1..0df8e0ad9e 100644 --- a/src/routes/relations.ts +++ b/src/routes/relations.ts @@ -4,6 +4,7 @@ import sanitizeQuery from '../middleware/sanitize-query'; import validateQuery from '../middleware/validate-query'; import * as RelationsService from '../services/relations'; import useCollection from '../middleware/use-collection'; +import * as ActivityService from '../services/activity'; const router = express.Router(); @@ -11,8 +12,18 @@ router.post( '/', useCollection('directus_relations'), asyncHandler(async (req, res) => { - const records = await RelationsService.createRelation(req.body, res.locals.query); - return res.json({ data: records }); + const item = await RelationsService.createRelation(req.body, res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -42,12 +53,22 @@ router.patch( '/:pk', useCollection('directus_relations'), asyncHandler(async (req, res) => { - const records = await RelationsService.updateRelation( + const item = await RelationsService.updateRelation( req.params.pk, req.body, res.locals.query ); - return res.json({ data: records }); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -56,6 +77,16 @@ router.delete( useCollection('directus_relations'), asyncHandler(async (req, res) => { await RelationsService.deleteRelation(req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: req.collection, + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/routes/roles.ts b/src/routes/roles.ts index 9427405df7..582c30801a 100644 --- a/src/routes/roles.ts +++ b/src/routes/roles.ts @@ -4,6 +4,7 @@ import sanitizeQuery from '../middleware/sanitize-query'; import validateQuery from '../middleware/validate-query'; import * as RolesService from '../services/roles'; import useCollection from '../middleware/use-collection'; +import * as ActivityService from '../services/activity'; const router = express.Router(); @@ -11,8 +12,18 @@ router.post( '/', useCollection('directus_roles'), asyncHandler(async (req, res) => { - const records = await RolesService.createRole(req.body, res.locals.query); - return res.json({ data: records }); + const item = await RolesService.createRole(req.body, res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -42,8 +53,18 @@ router.patch( '/:pk', useCollection('directus_roles'), asyncHandler(async (req, res) => { - const records = await RolesService.updateRole(req.params.pk, req.body, res.locals.query); - return res.json({ data: records }); + const item = await RolesService.updateRole(req.params.pk, req.body, res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -52,6 +73,16 @@ router.delete( useCollection('directus_roles'), asyncHandler(async (req, res) => { await RolesService.deleteRole(req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: req.collection, + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/routes/users.ts b/src/routes/users.ts index ba6097dcbf..3957276c08 100644 --- a/src/routes/users.ts +++ b/src/routes/users.ts @@ -6,6 +6,7 @@ import * as UsersService from '../services/users'; import Joi from '@hapi/joi'; import { InvalidPayloadException } from '../exceptions'; import useCollection from '../middleware/use-collection'; +import * as ActivityService from '../services/activity'; const router = express.Router(); @@ -13,8 +14,18 @@ router.post( '/', useCollection('directus_users'), asyncHandler(async (req, res) => { - const records = await UsersService.createUser(req.body, res.locals.query); - return res.json({ data: records }); + const item = await UsersService.createUser(req.body, res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -24,8 +35,18 @@ router.get( sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { - const records = await UsersService.readUsers(res.locals.query); - return res.json({ data: records }); + const item = await UsersService.readUsers(res.locals.query); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -54,6 +75,16 @@ router.delete( useCollection('directus_users'), asyncHandler(async (req, res) => { await UsersService.deleteUser(req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: req.collection, + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/routes/webhooks.ts b/src/routes/webhooks.ts index fe72666a17..4c93138906 100644 --- a/src/routes/webhooks.ts +++ b/src/routes/webhooks.ts @@ -4,6 +4,7 @@ import sanitizeQuery from '../middleware/sanitize-query'; import validateQuery from '../middleware/validate-query'; import * as WebhooksService from '../services/webhooks'; import useCollection from '../middleware/use-collection'; +import * as ActivityService from '../services/activity'; const router = express.Router(); @@ -11,8 +12,18 @@ router.post( '/', useCollection('directus_webhooks'), asyncHandler(async (req, res) => { - const records = await WebhooksService.createWebhook(req.body, res.locals.query); - return res.json({ data: records }); + const item = await WebhooksService.createWebhook(req.body, req.query); + + ActivityService.createActivity({ + action: ActivityService.Action.CREATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -22,7 +33,7 @@ router.get( sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { - const records = await WebhooksService.readWebhooks(res.locals.query); + const records = await WebhooksService.readWebhooks(req.query); return res.json({ data: records }); }) ); @@ -33,7 +44,7 @@ router.get( sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { - const record = await WebhooksService.readWebhook(req.params.pk, res.locals.query); + const record = await WebhooksService.readWebhook(req.params.pk, req.query); return res.json({ data: record }); }) ); @@ -42,12 +53,18 @@ router.patch( '/:pk', useCollection('directus_webhooks'), asyncHandler(async (req, res) => { - const records = await WebhooksService.updateWebhook( - req.params.pk, - req.body, - res.locals.query - ); - return res.json({ data: records }); + const item = await WebhooksService.updateWebhook(req.params.pk, req.body, req.query); + + ActivityService.createActivity({ + action: ActivityService.Action.UPDATE, + collection: req.collection, + item: item.id, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + + return res.json({ data: item }); }) ); @@ -56,6 +73,16 @@ router.delete( useCollection('directus_webhooks'), asyncHandler(async (req, res) => { await WebhooksService.deleteWebhook(req.params.pk); + + ActivityService.createActivity({ + action: ActivityService.Action.DELETE, + collection: req.collection, + item: req.params.pk, + ip: req.ip, + user_agent: req.get('user-agent'), + action_by: req.user, + }); + return res.status(200).end(); }) ); diff --git a/src/services/auth.ts b/src/services/auth.ts index 13fa68aad5..31325ee1d5 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -1,6 +1,6 @@ import database from '../database'; import jwt from 'jsonwebtoken'; -import bcrypt from 'bcrypt'; +import argon2 from 'argon2'; import { InvalidCredentialsException } from '../exceptions'; export const authenticate = async (email: string, password?: string) => { @@ -21,7 +21,7 @@ export const authenticate = async (email: string, password?: string) => { * email to leak anywhere else.. We might have to make a dedicated "copy" of this function to * signal the difference */ - if (password !== undefined && (await bcrypt.compare(password, user.password)) === false) { + if (password !== undefined && (await argon2.verify(password, user.password)) === false) { throw new InvalidCredentialsException(); } diff --git a/src/services/files.ts b/src/services/files.ts index 47f6a1ee90..21ed0c0712 100644 --- a/src/services/files.ts +++ b/src/services/files.ts @@ -45,7 +45,7 @@ export const createFile = async ( } await storage.disk(data.storage).put(data.filename_disk, stream as any); - await ItemsService.createItem('directus_files', payload, query); + return await ItemsService.createItem('directus_files', payload, query); }; export const readFiles = async (query: Query) => { @@ -64,13 +64,17 @@ export const updateFile = async ( query?: Query ) => { const payload = await PayloadService.processValues('update', 'directus_files', data); - await ItemsService.updateItem('directus_files', pk, payload, query); /** * @TODO * Handle changes in storage adapter -> going from local to S3 needs to delete from one, upload to the other */ + /** + * @TODO + * Extract metadata here too + */ + if (stream) { const file = await database .select('storage', 'filename_disk') @@ -81,6 +85,8 @@ export const updateFile = async ( // @todo type of stream in flydrive is wrong: https://github.com/Slynova-Org/flydrive/issues/145 await storage.disk(file.storage).put(file.filename_disk, stream as any); } + + return await ItemsService.updateItem('directus_files', pk, payload, query); }; export const deleteFile = async (pk: string | number) => { diff --git a/src/services/items.ts b/src/services/items.ts index d33f483c68..25ba488f04 100644 --- a/src/services/items.ts +++ b/src/services/items.ts @@ -76,7 +76,8 @@ export const updateItem = async ( data: Record, query: Query = {} ) => { - return await database(collection).update(data).where({ id: pk }); + const result = await database(collection).update(data).where({ id: pk }).returning('id'); + return readItem(collection, result[0], query); }; export const deleteItem = async (collection: string, pk: number | string) => { diff --git a/src/services/payload.ts b/src/services/payload.ts index 5a99765781..17dcb969a5 100644 --- a/src/services/payload.ts +++ b/src/services/payload.ts @@ -6,7 +6,7 @@ */ import { FieldInfo } from '../types/field'; -import bcrypt from 'bcrypt'; +import argon2 from 'argon2'; import { v4 as uuidv4 } from 'uuid'; import database from '../database'; import { clone } from 'lodash'; @@ -51,8 +51,10 @@ async function processField( } } -async function genHash(value: string | number) { - return await bcrypt.hash(value, Number(process.env.SALT_ROUNDS)); +async function genHash(value?: string | number) { + if (!value) return; + + return await argon2.hash(String(value)); } async function genUUID(operation: 'create' | 'update') { diff --git a/src/services/users.ts b/src/services/users.ts index ca2d43c50c..913d07276d 100644 --- a/src/services/users.ts +++ b/src/services/users.ts @@ -3,7 +3,7 @@ import * as ItemsService from './items'; import jwt from 'jsonwebtoken'; import { sendInviteMail } from '../mail'; import database from '../database'; -import bcrypt from 'bcrypt'; +import argon2 from 'argon2'; import * as PayloadService from '../services/payload'; import { InvalidPayloadException } from '../exceptions'; @@ -55,7 +55,7 @@ export const acceptInvite = async (token: string, password: string) => { throw new InvalidPayloadException(`Email address ${email} hasn't been invited.`); } - const passwordHashed = await bcrypt.hash(password, Number(process.env.SALT_ROUNDS)); + const passwordHashed = await argon2.hash(password); await database('directus_users') .update({ password: passwordHashed, status: 'active' })