diff --git a/server/controllers/UserController.ts b/server/controllers/UserController.ts index 583ccb2..3ed58d5 100644 --- a/server/controllers/UserController.ts +++ b/server/controllers/UserController.ts @@ -166,6 +166,38 @@ export class UserController { return true; } + /** + * Directly updates a User's email address without verification or validation. + * Intended for use by API actors with elevated permissions only. + * + * @param req - Incoming API request. + * @param res - Outgoing API response. + * @returns The fulfilled API response. + */ + public async updateUserEmailDirect(req: Request, res: Response): Promise { + const { uuid } = req.params as UserUUIDParams; + const { email } = req.body as CreateUserEmailChangeRequestBody; + + const foundUser = await User.findOne({ where: { uuid } }); + if (!foundUser) { + return errors.notFound(res); + } + + const existingUser = await User.findOne({ where: { email } }); + if (existingUser) { + return errors.badRequest(res, 'Email already in use.'); + } + + await foundUser.update({ email }); + + return res.send({ + data: { + central_identity_id: foundUser.uuid, + email, + }, + }); + } + /** * Creates a new EmailVerification opportunity for a user to change their email address. * diff --git a/server/routes/users.ts b/server/routes/users.ts index 18a9789..73c0c71 100644 --- a/server/routes/users.ts +++ b/server/routes/users.ts @@ -127,6 +127,15 @@ usersRouter.route('/:uuid/avatar').post( catchInternal((req, res) => controller.updateUserAvatar(req, res)), ); +usersRouter.route('/:uuid/email-change-direct').post( + verifyAPIAuthentication, + ensureActorIsAPIUser, + ensureAPIUserHasPermission(['users:write']), + validate(UserValidator.uuidParamSchema, 'params'), + validate(UserValidator.updateUserEmailDirectSchema, 'body'), + catchInternal((req, res) => controller.updateUserEmailDirect(req, res)), +); + usersRouter.route('/:uuid/email-change').post( verifyAPIAuthentication, ensureUserResourcePermission(true), diff --git a/server/swagger/swagger.json b/server/swagger/swagger.json index 83826fd..c46e767 100644 --- a/server/swagger/swagger.json +++ b/server/swagger/swagger.json @@ -620,6 +620,81 @@ } } } + }, + "/users/{uuid}/email-change-direct": { + "post": { + "summary": "Change a user's email directly without validation or verification. Intended for API actors with sufficent permissions.", + "operationId": "updateUserEmailDirect", + "tags": [ + "users" + ], + "parameters": [ + { + "in": "path", + "name": "uuid", + "schema": { + "type": "string" + }, + "required": true, + "description": "User UUID" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "uuid": { + "type": "string" + }, + "email": { + "type": "string" + } + } + } + } + }, + "400": { + "description": "Bad Request: Request is malformed or email is already in use", + "content": { + "application/json": { + "schema": { + "message": { + "type": "string" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "message": { + "type": "string" + } + } + } + } + } + } + } } }, "components": { diff --git a/server/validators/user.ts b/server/validators/user.ts index 394e162..56ebd1b 100644 --- a/server/validators/user.ts +++ b/server/validators/user.ts @@ -73,6 +73,10 @@ export const updateUserEmailSchema = joi.object({ email: joi.string().email().required(), }); +export const updateUserEmailDirectSchema = joi.object({ + email: joi.string().email().required(), +}); + export const updateUserOrganizationAdminRoleSchema = joi.object({ admin_role: joi.string().valid(...Object.keys(UserOrganizationAdminRoleEnum)).required(), });