diff --git a/src/routes/users.ts b/src/routes/users.ts index bc1b8bc8a0..21248f0859 100644 --- a/src/routes/users.ts +++ b/src/routes/users.ts @@ -6,25 +6,21 @@ import * as UsersService from '../services/users'; import Joi from '@hapi/joi'; import { InvalidPayloadException, InvalidCredentialsException } from '../exceptions'; import useCollection from '../middleware/use-collection'; -import * as ActivityService from '../services/activity'; const router = express.Router(); router.post( '/', useCollection('directus_users'), + sanitizeQuery, + validateQuery, asyncHandler(async (req, res) => { - const item = await UsersService.createUser(req.body, req.sanitizedQuery); - - ActivityService.createActivity({ - action: ActivityService.Action.CREATE, - collection: req.collection, - item: item.id, + const primaryKey = await UsersService.createUser(req.body, { ip: req.ip, - user_agent: req.get('user-agent'), - action_by: req.user, + userAgent: req.get('user-agent'), + user: req.user, }); - + const item = await UsersService.readUser(primaryKey, req.sanitizedQuery); return res.json({ data: item }); }) ); @@ -36,7 +32,6 @@ router.get( validateQuery, asyncHandler(async (req, res) => { const item = await UsersService.readUsers(req.sanitizedQuery); - return res.json({ data: item }); }) ); @@ -77,17 +72,13 @@ router.patch( throw new InvalidCredentialsException(); } - const item = await UsersService.updateUser(req.user, req.body, req.sanitizedQuery); - - ActivityService.createActivity({ - action: ActivityService.Action.UPDATE, - collection: req.collection, - item: item.id, + const primaryKey = await UsersService.updateUser(req.user, req.body, { ip: req.ip, - user_agent: req.get('user-agent'), - action_by: req.user, + userAgent: req.get('user-agent'), + user: req.user, }); + const item = await UsersService.readUser(primaryKey, req.sanitizedQuery); return res.json({ data: item }); }) ); @@ -95,18 +86,15 @@ router.patch( router.patch( '/:pk', useCollection('directus_users'), + sanitizeQuery, + validateQuery, asyncHandler(async (req, res) => { - const item = await UsersService.updateUser(req.params.pk, req.body, req.sanitizedQuery); - - ActivityService.createActivity({ - action: ActivityService.Action.UPDATE, - collection: req.collection, - item: item.id, + const primaryKey = await UsersService.updateUser(req.params.pk, req.body, { ip: req.ip, - user_agent: req.get('user-agent'), - action_by: req.user, + userAgent: req.get('user-agent'), + user: req.user, }); - + const item = await UsersService.readUser(primaryKey, req.sanitizedQuery); return res.json({ data: item }); }) ); @@ -115,15 +103,10 @@ router.delete( '/:pk', 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, + await UsersService.deleteUser(req.params.pk, { ip: req.ip, - user_agent: req.get('user-agent'), - action_by: req.user, + userAgent: req.get('user-agent'), + user: req.user, }); return res.status(200).end(); @@ -141,7 +124,11 @@ router.post( asyncHandler(async (req, res) => { const { error } = inviteSchema.validate(req.body); if (error) throw new InvalidPayloadException(error.message); - await UsersService.inviteUser(req.body.email, req.body.role); + await UsersService.inviteUser(req.body.email, req.body.role, { + ip: req.ip, + userAgent: req.get('user-agent'), + user: req.user, + }); res.end(); }) ); diff --git a/src/services/users.ts b/src/services/users.ts index 2c01b39711..50afd35af9 100644 --- a/src/services/users.ts +++ b/src/services/users.ts @@ -1,14 +1,13 @@ -import { Query } from '../types/query'; import * as ItemsService from './items'; import jwt from 'jsonwebtoken'; import { sendInviteMail } from '../mail'; import database from '../database'; import argon2 from 'argon2'; import { InvalidPayloadException } from '../exceptions'; +import { Accountability, Query } from '../types'; -export const createUser = async (data: Record, query?: Query) => { - const primaryKey = await ItemsService.createItem('directus_users', data); - return await ItemsService.readItem('directus_users', primaryKey, query); +export const createUser = async (data: Record, accountability: Accountability) => { + return await ItemsService.createItem('directus_users', data, accountability); }; export const readUsers = async (query?: Query) => { @@ -19,23 +18,26 @@ export const readUser = async (pk: string | number, query?: Query) => { return await ItemsService.readItem('directus_users', pk, query); }; -export const updateUser = async (pk: string | number, data: Record, query?: Query) => { +export const updateUser = async ( + pk: string | number, + data: Record, + accountability: Accountability +) => { /** * @todo * Remove "other" refresh token sessions when changing password to enforce "logout everywhere" on password change * * Maybe make this an option? */ - const primaryKey = await ItemsService.updateItem('directus_users', pk, data); - return await ItemsService.readItem('directus_users', primaryKey, query); + return await ItemsService.updateItem('directus_users', pk, data, accountability); }; -export const deleteUser = async (pk: string | number) => { - await ItemsService.deleteItem('directus_users', pk); +export const deleteUser = async (pk: string | number, accountability: Accountability) => { + await ItemsService.deleteItem('directus_users', pk, accountability); }; -export const inviteUser = async (email: string, role: string) => { - await createUser({ email, role, status: 'invited' }); +export const inviteUser = async (email: string, role: string, accountability: Accountability) => { + await createUser({ email, role, status: 'invited' }, accountability); const payload = { email }; const token = jwt.sign(payload, process.env.SECRET, { expiresIn: '7d' }); @@ -45,7 +47,7 @@ export const inviteUser = async (email: string, role: string) => { }; export const acceptInvite = async (token: string, password: string) => { - const { email } = jwt.verify(token, process.env.SECRET) as Record; + const { email } = jwt.verify(token, process.env.SECRET) as { email: string }; const user = await database .select('id', 'status') .from('directus_users')