diff --git a/.changeset/salty-suits-strive.md b/.changeset/salty-suits-strive.md new file mode 100644 index 0000000000000..e9506dc744605 --- /dev/null +++ b/.changeset/salty-suits-strive.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes missing permission check on the `POST /api/v1/fingerprint` endpoint diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index cf7a9a2d3d1bf..d7b0be701a3f5 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -11,6 +11,7 @@ import { isMeteorCall, meSuccessResponseSchema, validateUnauthorizedErrorResponse, + validateForbiddenErrorResponse, validateBadRequestErrorResponse, } from '@rocket.chat/rest-typings'; import type { MeApiSuccessResponse } from '@rocket.chat/rest-typings'; @@ -795,10 +796,12 @@ API.v1.post( 'fingerprint', { authRequired: true, + permissionsRequired: ['manage-cloud'], body: isFingerprintProps, response: { 200: fingerprintResponseSchema, 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, 400: validateBadRequestErrorResponse, }, }, diff --git a/apps/meteor/tests/end-to-end/api/miscellaneous.ts b/apps/meteor/tests/end-to-end/api/miscellaneous.ts index 0f3f7de85abfd..1196c95e24a49 100644 --- a/apps/meteor/tests/end-to-end/api/miscellaneous.ts +++ b/apps/meteor/tests/end-to-end/api/miscellaneous.ts @@ -686,4 +686,51 @@ describe('miscellaneous', () => { .end(done); }); }); + + describe('/fingerprint', () => { + let unauthorizedUser: TestUser; + let unauthorizedUserCredentials: Credentials; + + before(async () => { + unauthorizedUser = await createUser(); + unauthorizedUserCredentials = await doLogin(unauthorizedUser.username, password); + }); + + after(async () => { + await deleteUser(unauthorizedUser); + }); + + it('should return 401 when called without authentication', async () => { + const res = await request.post(api('fingerprint')).send({ setDeploymentAs: 'updated-configuration' }); + + expect(res.status).to.equal(401); + expect(res.body).to.have.property('status', 'error'); + }); + + it('should return 403 when a user without the manage-cloud permission tries to acknowledge a deployment configuration change', async () => { + const res = await request + .post(api('fingerprint')) + .set(unauthorizedUserCredentials) + .send({ setDeploymentAs: 'updated-configuration' }); + + expect(res.status).to.equal(403); + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]'); + }); + + it('should return 403 when a user without the manage-cloud permission tries to deregister the workspace as a new workspace', async () => { + const res = await request.post(api('fingerprint')).set(unauthorizedUserCredentials).send({ setDeploymentAs: 'new-workspace' }); + + expect(res.status).to.equal(403); + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]'); + }); + + it('should return 200 when a user with the manage-cloud permission acknowledges a deployment configuration change', async () => { + const res = await request.post(api('fingerprint')).set(credentials).send({ setDeploymentAs: 'updated-configuration' }); + + expect(res.status).to.equal(200); + expect(res.body).to.have.property('success', true); + }); + }); });