diff --git a/.changeset/wise-regions-sip.md b/.changeset/wise-regions-sip.md new file mode 100644 index 0000000000000..00dff608d60be --- /dev/null +++ b/.changeset/wise-regions-sip.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes an issue on `canAccessRoom` where `abacAttributes` were not fetched in some endpoint calls diff --git a/.github/actions/update-version-durability/package-lock.json b/.github/actions/update-version-durability/package-lock.json index 6434bbc5bf843..3465de9655c90 100644 --- a/.github/actions/update-version-durability/package-lock.json +++ b/.github/actions/update-version-durability/package-lock.json @@ -12,7 +12,7 @@ "@actions/core": "^1.10.1", "@octokit/rest": "^21.0.0", "@xmldom/xmldom": "^0.8.13", - "axios": "^1.7.2", + "axios": "^1.16.0", "beauty-html": "^1.3.1", "colors": "^1.4.0", "diff": "^5.1.0", @@ -218,6 +218,18 @@ "node": ">=10.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -225,13 +237,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", - "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz", + "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -278,6 +291,23 @@ "node": ">= 0.8" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -504,6 +534,19 @@ "node": ">= 0.4" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -534,6 +577,12 @@ "node": ">= 0.6" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", diff --git a/.github/actions/update-version-durability/package.json b/.github/actions/update-version-durability/package.json index ba7dee8a6c92d..b7483014396c9 100644 --- a/.github/actions/update-version-durability/package.json +++ b/.github/actions/update-version-durability/package.json @@ -11,7 +11,7 @@ "dependencies": { "@actions/core": "^1.10.1", "@octokit/rest": "^21.0.0", - "axios": "^1.7.2", + "axios": "^1.16.0", "beauty-html": "^1.3.1", "colors": "^1.4.0", "diff": "^5.1.0", diff --git a/apps/meteor/app/authorization/server/functions/canAccessRoom.ts b/apps/meteor/app/authorization/server/functions/canAccessRoom.ts index 9a20cda8dffa1..152e2bfd87d88 100644 --- a/apps/meteor/app/authorization/server/functions/canAccessRoom.ts +++ b/apps/meteor/app/authorization/server/functions/canAccessRoom.ts @@ -7,4 +7,5 @@ export const roomAccessAttributes = { t: 1, teamId: 1, prid: 1, + abacAttributes: 1, }; diff --git a/apps/meteor/tests/end-to-end/api/abac.ts b/apps/meteor/tests/end-to-end/api/abac.ts index c0eaef4208b69..0ec6f1ea0c15e 100644 --- a/apps/meteor/tests/end-to-end/api/abac.ts +++ b/apps/meteor/tests/end-to-end/api/abac.ts @@ -2093,9 +2093,10 @@ const addAbacAttributesToUserDirectly = async (userId: string, abacAttributes: I .get(`${v1}/groups.history`) .set(cacheUserCreds) .query({ roomId: cacheRoom._id }) - .expect(403) + .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('errorType', 'error-room-not-found'); }); }); @@ -2142,6 +2143,87 @@ const addAbacAttributesToUserDirectly = async (userId: string, abacAttributes: I }); }); + describe('Room access via projection-path endpoints (groups.messages)', () => { + // Unlike groups.history (which re-loads the full room), groups.messages gates access + // solely through the shared `roomAccessAttributes` projection. That projection must carry + // `abacAttributes`, otherwise canAccessRoom falls back to the subscription check and a + // non-compliant but still-subscribed member can read messages. + let projRoom: IRoom; + const projAttrKey = `proj_access_attr_${Date.now()}`; + let projUser: IUser; + let projUserCreds: Credentials; + + before(async function () { + this.timeout(10000); + + await request + .post(`${v1}/abac/attributes`) + .set(credentials) + .send({ key: projAttrKey, values: ['on'] }) + .expect(200); + + projRoom = (await createRoom({ type: 'p', name: `abac-proj-room-${Date.now()}` })).body.group; + + projUser = await createUser(); + projUserCreds = await login(projUser.username, password); + await addAbacAttributesToUserDirectly(projUser._id, [{ key: projAttrKey, values: ['on'] }]); + await addAbacAttributesToUserDirectly(credentials['X-User-Id'], [{ key: projAttrKey, values: ['on'] }]); + + await request + .post(`${v1}/abac/rooms/${projRoom._id}/attributes/${projAttrKey}`) + .set(credentials) + .send({ values: ['on'] }) + .expect(200); + + await request + .post(`${v1}/groups.invite`) + .set(credentials) + .send({ roomId: projRoom._id, usernames: [projUser.username] }) + .expect(200); + + await request + .post(`${v1}/chat.sendMessage`) + .set(credentials) + .send({ message: { rid: projRoom._id, msg: 'Seed message for projection access test' } }) + .expect(200); + + // Evaluate every access check against the PDP, no cached decisions + await updateSetting('Abac_Cache_Decision_Time_Seconds', 0); + }); + + after(async () => { + await deleteRoom({ type: 'p', roomId: projRoom._id }); + await deleteUser(projUser); + await updateSetting('Abac_Cache_Decision_Time_Seconds', 300); + }); + + it('PROJECTION: compliant member can read messages', async () => { + await request + .get(`${v1}/groups.messages`) + .set(projUserCreds) + .query({ roomId: projRoom._id }) + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').that.is.an('array'); + }); + }); + + it('PROJECTION: member that lost its attributes is denied while still subscribed', async () => { + await addAbacAttributesToUserDirectly(projUser._id, []); + + await request + .get(`${v1}/groups.messages`) + .set(projUserCreds) + .query({ roomId: projRoom._id }) + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('errorType', 'error-room-not-found'); + }); + }); + }); + describe('list abac rooms', () => { const listAttrKey1 = `list_attr_1_${Date.now()}`; const listAttrKey2 = `list_attr_2_${Date.now()}`; @@ -3178,13 +3260,18 @@ const addAbacAttributesToUserDirectly = async (userId: string, abacAttributes: I .get('/api/v1/groups.history') .set(userCreds) .query({ roomId: room._id }) - .expect(403) + .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('errorType', 'error-room-not-found'); }); }); it('user is removed from room after access DENY', async () => { + await mockServerReset(); + await seedDefaultMocks(); + await seedGetDecisionBulk([{ resourceDecisions: [{ decision: 'DECISION_PERMIT', ephemeralResourceId: room._id }] }]); + const res = await request.get('/api/v1/groups.members').set(credentials).query({ roomId: room._id }).expect(200); const usernames = res.body.members.map((m: IUser) => m.username); @@ -3257,6 +3344,10 @@ const addAbacAttributesToUserDirectly = async (userId: string, abacAttributes: I }); it('denied user is not a member of the room', async () => { + await mockServerReset(); + await seedDefaultMocks(); + await seedGetDecisionBulk([{ resourceDecisions: [{ decision: 'DECISION_PERMIT', ephemeralResourceId: room._id }] }]); + const res = await request.get('/api/v1/groups.members').set(credentials).query({ roomId: room._id }).expect(200); const usernames = res.body.members.map((m: IUser) => m.username); @@ -3316,9 +3407,10 @@ const addAbacAttributesToUserDirectly = async (userId: string, abacAttributes: I .get('/api/v1/groups.history') .set(userCredentials) .query({ roomId: room._id }) - .expect(403) + .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('errorType', 'error-room-not-found'); }); }); diff --git a/package.json b/package.json index 2d34e6b121f74..0fdd57551a517 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,11 @@ "external-editor/tmp": "0.2.6", "@sematext/gc-stats@npm:^1.5.9": "patch:@sematext/gc-stats@npm%3A1.5.9#~/.yarn/patches/@sematext-gc-stats-npm-1.5.9-01e77be4d0.patch", "adm-zip": "0.5.9", - "axios@npm:^1.6.1": "1.15.2", - "axios@npm:^1.7.4": "1.15.2", - "axios@npm:^1.7.8": "1.15.2", - "axios@npm:^1.11.0": "1.15.2", - "axios@npm:^1.13.2": "1.15.2", + "axios@npm:^1.6.1": "1.16.0", + "axios@npm:^1.7.4": "1.16.0", + "axios@npm:^1.7.8": "1.16.0", + "axios@npm:^1.11.0": "1.16.0", + "axios@npm:^1.13.2": "1.16.0", "sass/immutable": "5.1.5", "jws@npm:^3.2.2": "3.2.3", "jws@npm:^4.0.0": "4.0.1", @@ -70,7 +70,7 @@ "minimist": "1.2.6", "mongodb": "6.10.0", "protobufjs": "7.5.8", - "webdav/axios": "0.31.1", + "webdav/axios": "0.32.0", "flatted@npm:^3.1.0": "3.4.2", "flatted@npm:^3.2.9": "3.4.2", "flatted@npm:^3.3.1": "3.4.2", @@ -87,6 +87,8 @@ "picomatch@npm:^4.0.3": "^4.0.4", "path-to-regexp@npm:^8.1.0": "^8.4.0", "serialize-javascript": "^7.0.5", + "launch-editor/shell-quote": "1.8.4", + "npm-run-all/shell-quote": "1.8.4", "underscore": "1.13.8", "undici@npm:^6.19.5": "^6.24.0", "webpack": "~5.104.0", diff --git a/yarn.lock b/yarn.lock index 63d36d8332850..9b18fe736edaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16997,25 +16997,25 @@ __metadata: languageName: node linkType: hard -"axios@npm:0.31.1": - version: 0.31.1 - resolution: "axios@npm:0.31.1" +"axios@npm:0.32.0": + version: 0.32.0 + resolution: "axios@npm:0.32.0" dependencies: follow-redirects: "npm:^1.15.4" form-data: "npm:^4.0.4" proxy-from-env: "npm:^1.1.0" - checksum: 10/097dffdc0ab1229fd2f2e792d038e81272568e8280217933f4ad68234761ffebe2b51468f6190f5d98eb3df9511800083f74f78dda4c6229b24b4f9d98d300e5 + checksum: 10/1e35bb0778507851d32587176f72e9dbd9b091e4e7d59b8b60ce77c78fe1663f2b2e949478db1f71998ce68b3eb94df3031fcce110ce4e1c637474c50119e0a1 languageName: node linkType: hard -"axios@npm:1.15.2": - version: 1.15.2 - resolution: "axios@npm:1.15.2" +"axios@npm:1.16.0": + version: 1.16.0 + resolution: "axios@npm:1.16.0" dependencies: - follow-redirects: "npm:^1.15.11" + follow-redirects: "npm:^1.16.0" form-data: "npm:^4.0.5" proxy-from-env: "npm:^2.1.0" - checksum: 10/eebbd8cb777316d4252cd994a06ec9fb956ef519214a62dab6c5443ae8b753b5116e9a770502316789e6cdef1101e6aae53b6936d6a3791b2d66d75f4d7d2462 + checksum: 10/cf8b521ff732c21550b38c8739aef556ea5e14b268468bb89e4307416ab262b462e582474e810963a3d61c2392ab47ad35c11d05eff027de7c97113bc7411623 languageName: node linkType: hard @@ -34048,10 +34048,10 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.4.3, shell-quote@npm:^1.6.1, shell-quote@npm:^1.8.1": - version: 1.8.1 - resolution: "shell-quote@npm:1.8.1" - checksum: 10/af19ab5a1ec30cb4b2f91fd6df49a7442d5c4825a2e269b3712eded10eedd7f9efeaab96d57829880733fc55bcdd8e9b1d8589b4befb06667c731d08145e274d +"shell-quote@npm:1.8.4": + version: 1.8.4 + resolution: "shell-quote@npm:1.8.4" + checksum: 10/a3e3796385f2cd5cf0b78207a4439f0c7395c0833fc75b2473084b5d298c109c5c0fa687fcd1c04e4b4484866e5bb8eaae7efae443b80fff71ea7e29baf11f0c languageName: node linkType: hard