{"record":{"id":"07f018e58aab3591","repo":"RocketChat/Rocket.Chat","slug":"not-authorized-07f018","errorCode":"not_authorized","errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/integrations/outgoing/updateOutgoingIntegration.ts","lineNumber":46,"sourceCode":"\tconst integration = await validateOutgoingIntegration(_integration, userId);\n\n\tif (!integration.token || integration.token.trim() === '') {\n\t\tthrow new Meteor.Error('error-invalid-token', 'Invalid token', {\n\t\t\tmethod: 'updateOutgoingIntegration',\n\t\t});\n\t}\n\n\tlet currentIntegration: IIntegration | null;\n\n\tif (await hasPermissionAsync(userId, 'manage-outgoing-integrations')) {\n\t\tcurrentIntegration = await Integrations.findOneById(integrationId);\n\t} else if (await hasPermissionAsync(userId, 'manage-own-outgoing-integrations')) {\n\t\tcurrentIntegration = await Integrations.findOne({\n\t\t\t'_id': integrationId,\n\t\t\t'_createdBy._id': userId,\n\t\t});\n\t} else {\n\t\tthrow new Meteor.Error('not_authorized', 'Unauthorized', {\n\t\t\tmethod: 'updateOutgoingIntegration',\n\t\t});\n\t}\n\n\tif (!currentIntegration) {\n\t\tthrow new Meteor.Error('invalid_integration', '[methods] updateOutgoingIntegration -> integration not found');\n\t}\n\n\tconst oldScriptEngine = currentIntegration.scriptEngine;\n\tconst scriptEngine = integration.scriptEngine ?? oldScriptEngine ?? 'isolated-vm';\n\tif (\n\t\tintegration.script?.trim() &&\n\t\t(scriptEngine !== oldScriptEngine || integration.script?.trim() !== currentIntegration.script?.trim())\n\t) {\n\t\twrapExceptions(() => validateScriptEngine(scriptEngine)).catch((e) => {\n\t\t\tthrow new Meteor.Error(e.message);\n\t\t});\n\t}","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/integrations/outgoing/updateOutgoingIntegration.ts#L28-L64","documentation":"While resolving the current integration, updateOutgoingIntegration branches on permissions: 'manage-outgoing-integrations' allows findOneById on any integration, 'manage-own-outgoing-integrations' scopes the query to _createdBy._id === userId, and holding neither throws not_authorized 'Unauthorized'. Note this throw happens before the not-found check, so an unauthorized caller cannot distinguish 'no permission' from 'does not exist'.","triggerScenarios":"Calling updateOutgoingIntegration (DDP or PUT /v1/integrations.update) with a user whose roles lack both integration permissions; permissions revoked after the admin UI was loaded.","commonSituations":"Non-admin users editing outgoing webhooks without manage-own-outgoing-integrations; stale sessions after role changes; API tokens belonging to under-privileged users.","solutions":["Grant 'manage-outgoing-integrations' or 'manage-own-outgoing-integrations' to the caller's role","Re-login/refresh so the new permission reaches the session or token context","Retry with an admin-owned API token for the PUT /v1/integrations.update call"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const canManageAll = await hasPermissionAsync(uid, 'manage-outgoing-integrations');\nconst canManageOwn = !canManageAll && (await hasPermissionAsync(uid, 'manage-own-outgoing-integrations'));\nif (!canManageAll && !canManageOwn) {\n  throw new Meteor.Error('not_authorized', 'Missing integration permissions');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await Meteor.callAsync('updateOutgoingIntegration', id, payload);\n} catch (err) {\n  if (err instanceof Meteor.Error && err.error === 'not_authorized') {\n    // request one of the two manage permissions; do not retry unchanged\n    return;\n  }\n  throw err;\n}","preventionTips":["Gate outgoing-integration edit UI on the manage permissions","Remember manage-own only reaches integrations you created","Refresh sessions and tokens after permission changes"],"tags":["permissions","integrations","outgoing-integrations"],"backgroundTag":"permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}