From b7a3ae183d4d216d39ec0b5f4dd5f307a046d0e1 Mon Sep 17 00:00:00 2001 From: gustrb Date: Thu, 13 Feb 2025 14:09:01 -0300 Subject: [PATCH 1/5] chore: adds a new omnigateway setting --- apps/meteor/server/settings/setup-wizard.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/meteor/server/settings/setup-wizard.ts b/apps/meteor/server/settings/setup-wizard.ts index 13d76a6070d92..5082348886a65 100644 --- a/apps/meteor/server/settings/setup-wizard.ts +++ b/apps/meteor/server/settings/setup-wizard.ts @@ -1223,6 +1223,12 @@ export const createSetupWSettings = () => secret: true, }); + await this.add('Omnigateway_Url', 'https://omni-gateway.rocket.chat', { + type: 'string', + hidden: true, + readonly: true, + }); + await this.add('Cloud_Service_Agree_PrivacyTerms', false, { type: 'boolean', }); From f31e278e2ddd12c93619bb0aa707fd2451f452ed Mon Sep 17 00:00:00 2001 From: gustrb Date: Thu, 13 Feb 2025 16:19:26 -0300 Subject: [PATCH 2/5] feat: add hiddenSettings capabilities for the permission --- .../app/apps/server/bridges/settings.ts | 25 +++++++++++++++++-- .../app/apps/server/converters/settings.js | 2 +- apps/meteor/server/settings/setup-wizard.ts | 1 + .../src/definition/permissions/IPermission.ts | 4 +++ .../src/server/permissions/AppPermissions.ts | 4 +-- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/apps/meteor/app/apps/server/bridges/settings.ts b/apps/meteor/app/apps/server/bridges/settings.ts index b3e6aaff32a47..d7a63a1305aef 100644 --- a/apps/meteor/app/apps/server/bridges/settings.ts +++ b/apps/meteor/app/apps/server/bridges/settings.ts @@ -1,4 +1,5 @@ -import type { IAppServerOrchestrator } from '@rocket.chat/apps'; +import { Apps, type IAppServerOrchestrator } from '@rocket.chat/apps'; +import type { IReadSettingPermission } from '@rocket.chat/apps-engine/definition/permissions/IPermission'; import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import { ServerSettingBridge } from '@rocket.chat/apps-engine/server/bridges/ServerSettingBridge'; import { Settings } from '@rocket.chat/models'; @@ -47,7 +48,27 @@ export class AppSettingBridge extends ServerSettingBridge { protected async isReadableById(id: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is checking if they can read the setting ${id}.`); const setting = await Settings.findOneById(id); - return Boolean(setting && !setting.secret); + + if (!setting) { + return false; + } + + // Get the server-setting.read permission + const app = Apps.self?.getManager().getOneById(appId); + if (!app) { + return false; + } + + const settingsPerms = app?.getInfo().permissions?.find((perm) => perm.name === 'server-setting.read'); + if (!settingsPerms) { + return false; + } + + if ((setting.secret || setting.hidden) && (settingsPerms as IReadSettingPermission).hiddenSettings?.includes(id)) { + return true; + } + + return !setting.secret; } protected async updateOne(setting: ISetting & { id: string }, appId: string): Promise { diff --git a/apps/meteor/app/apps/server/converters/settings.js b/apps/meteor/app/apps/server/converters/settings.js index da3e075deb678..07b790cb7c592 100644 --- a/apps/meteor/app/apps/server/converters/settings.js +++ b/apps/meteor/app/apps/server/converters/settings.js @@ -7,7 +7,7 @@ export class AppSettingsConverter { } async convertById(settingId) { - const setting = await Settings.findOneNotHiddenById(settingId); + const setting = await Settings.findOneById(settingId); return this.convertToApp(setting); } diff --git a/apps/meteor/server/settings/setup-wizard.ts b/apps/meteor/server/settings/setup-wizard.ts index 5082348886a65..a63b4cfe49c66 100644 --- a/apps/meteor/server/settings/setup-wizard.ts +++ b/apps/meteor/server/settings/setup-wizard.ts @@ -1226,6 +1226,7 @@ export const createSetupWSettings = () => await this.add('Omnigateway_Url', 'https://omni-gateway.rocket.chat', { type: 'string', hidden: true, + secret: true, readonly: true, }); diff --git a/packages/apps-engine/src/definition/permissions/IPermission.ts b/packages/apps-engine/src/definition/permissions/IPermission.ts index af36c2f63624a..8618d40afeef5 100644 --- a/packages/apps-engine/src/definition/permissions/IPermission.ts +++ b/packages/apps-engine/src/definition/permissions/IPermission.ts @@ -10,3 +10,7 @@ export interface INetworkingPermission extends IPermission { export interface IWorkspaceTokenPermission extends IPermission { scopes: Array; } + +export interface IReadSettingPermission extends IPermission { + hiddenSettings: Array; +} diff --git a/packages/apps-engine/src/server/permissions/AppPermissions.ts b/packages/apps-engine/src/server/permissions/AppPermissions.ts index fe368668b32be..80f74838b95aa 100644 --- a/packages/apps-engine/src/server/permissions/AppPermissions.ts +++ b/packages/apps-engine/src/server/permissions/AppPermissions.ts @@ -1,4 +1,4 @@ -import type { INetworkingPermission, IPermission, IWorkspaceTokenPermission } from '../../definition/permissions/IPermission'; +import type { INetworkingPermission, IPermission, IReadSettingPermission, IWorkspaceTokenPermission } from '../../definition/permissions/IPermission'; /** * @description @@ -33,7 +33,7 @@ export const AppPermissions = { registerButtons: { name: 'ui.registerButtons' }, }, setting: { - read: { name: 'server-setting.read' }, + read: { name: 'server-setting.read', hiddenSettings: [] } as IReadSettingPermission, write: { name: 'server-setting.write' }, }, room: { From cd5b2d768cf4d146af72d40f6694d669de453201 Mon Sep 17 00:00:00 2001 From: Gustavo Reis Bauer Date: Fri, 14 Feb 2025 09:11:44 -0300 Subject: [PATCH 3/5] Create tough-eggs-camp.md --- .changeset/tough-eggs-camp.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/tough-eggs-camp.md diff --git a/.changeset/tough-eggs-camp.md b/.changeset/tough-eggs-camp.md new file mode 100644 index 0000000000000..10616da2b6675 --- /dev/null +++ b/.changeset/tough-eggs-camp.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/apps-engine": minor +--- + +Enables specifying hidden settings that are enabled to be accessed through the apps-engine in the permission list. From dd4bdb4e610c6e3063cecf8462c68d289f06957d Mon Sep 17 00:00:00 2001 From: gustrb Date: Fri, 14 Feb 2025 14:49:50 -0300 Subject: [PATCH 4/5] chore: avoid querying the database more than needed --- .../app/apps/server/bridges/settings.ts | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/apps/meteor/app/apps/server/bridges/settings.ts b/apps/meteor/app/apps/server/bridges/settings.ts index d7a63a1305aef..92b7dfa3b91db 100644 --- a/apps/meteor/app/apps/server/bridges/settings.ts +++ b/apps/meteor/app/apps/server/bridges/settings.ts @@ -22,11 +22,12 @@ export class AppSettingBridge extends ServerSettingBridge { protected async getOneById(id: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is getting the setting by id ${id}.`); - if (!(await this.isReadableById(id, appId))) { + const setting = await this.getReadableSettingById(id, appId); + if (!setting) { throw new Error(`The setting "${id}" is not readable.`); } - return this.orch.getConverters()?.get('settings').convertById(id); + return setting; } protected async hideGroup(name: string, appId: string): Promise { @@ -48,27 +49,40 @@ export class AppSettingBridge extends ServerSettingBridge { protected async isReadableById(id: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is checking if they can read the setting ${id}.`); const setting = await Settings.findOneById(id); + return Boolean(setting && !setting.secret); + } - if (!setting) { - return false; - } - - // Get the server-setting.read permission + protected async getReadableSettingById(id: string, appId: string): Promise { + this.orch.debugLog(`The app ${appId} is checking if they can read the setting ${id}`); const app = Apps.self?.getManager().getOneById(appId); if (!app) { - return false; + this.orch.debugLog(`The app ${appId} is not found.`); + return null; + } + + const { permissions } = app.getInfo(); + if (!permissions) { + this.orch.debugLog(`The app ${appId} has no configured permissions.`); + return null; } - const settingsPerms = app?.getInfo().permissions?.find((perm) => perm.name === 'server-setting.read'); - if (!settingsPerms) { - return false; + const readSettingsPermission = permissions.find((perm) => perm.name === 'server-setting.read'); + if (!readSettingsPermission) { + this.orch.debugLog(`The app ${appId} has no server-setting.read permission.`); + return null; } - if ((setting.secret || setting.hidden) && (settingsPerms as IReadSettingPermission).hiddenSettings?.includes(id)) { - return true; + const readSettings = readSettingsPermission as IReadSettingPermission; + // If the setting is in the hiddenSettings list (defined within the permission), then it can bypass the hidden flag. + // If not, then it must be a non-hidden setting. This is to allow apps to read hidden settings if they have the permission to do so. + const setting = readSettings.hiddenSettings?.includes(id) ? await Settings.findOneById(id) : await Settings.findOneNotHiddenById(id); + + if (!setting) { + this.orch.debugLog(`The setting ${id} is not found.`); + return null; } - return !setting.secret; + return this.orch.getConverters()?.get('settings').convertToApp(setting); } protected async updateOne(setting: ISetting & { id: string }, appId: string): Promise { From 2827d6ca46f617ffef9e141450c32d38da2ae68c Mon Sep 17 00:00:00 2001 From: Gustavo Reis Bauer Date: Mon, 17 Feb 2025 13:17:41 -0300 Subject: [PATCH 5/5] Update apps/meteor/app/apps/server/bridges/settings.ts Co-authored-by: Kevin Aleman --- apps/meteor/app/apps/server/bridges/settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/apps/server/bridges/settings.ts b/apps/meteor/app/apps/server/bridges/settings.ts index 92b7dfa3b91db..5a5b1d902d4a9 100644 --- a/apps/meteor/app/apps/server/bridges/settings.ts +++ b/apps/meteor/app/apps/server/bridges/settings.ts @@ -53,7 +53,7 @@ export class AppSettingBridge extends ServerSettingBridge { } protected async getReadableSettingById(id: string, appId: string): Promise { - this.orch.debugLog(`The app ${appId} is checking if they can read the setting ${id}`); + this.orch.debugLog(`The app ${appId} is checking if it can read the setting ${id}`); const app = Apps.self?.getManager().getOneById(appId); if (!app) { this.orch.debugLog(`The app ${appId} is not found.`);