From c0fa1c884cccab47f4e68dd81457c424cf176f11 Mon Sep 17 00:00:00 2001 From: Gustavo Reis Bauer Date: Wed, 29 Jan 2025 21:24:16 -0300 Subject: [PATCH] fix: Wrong marketplaceInfo value being stored in the database (#35009) Co-authored-by: Douglas Gubert <1810309+d-gubert@users.noreply.github.com> --- .changeset/grumpy-lemons-pull.md | 10 +++++++ .../ee/app/license/server/canEnableApp.ts | 3 +- .../ee/server/apps/communication/rest.ts | 13 +++++++-- .../ee/server/apps/marketplace/appInfo.ts | 28 ------------------- .../app/license/server/canEnableApp.spec.ts | 2 +- packages/apps-engine/src/server/AppManager.ts | 6 ++-- .../src/server/managers/AppLicenseManager.ts | 9 +++--- .../src/server/storage/IAppStorageItem.ts | 2 +- 8 files changed, 33 insertions(+), 40 deletions(-) create mode 100644 .changeset/grumpy-lemons-pull.md delete mode 100644 apps/meteor/ee/server/apps/marketplace/appInfo.ts diff --git a/.changeset/grumpy-lemons-pull.md b/.changeset/grumpy-lemons-pull.md new file mode 100644 index 000000000000..20afbc6c7098 --- /dev/null +++ b/.changeset/grumpy-lemons-pull.md @@ -0,0 +1,10 @@ +--- +'@rocket.chat/model-typings': patch +'@rocket.chat/rest-typings': patch +'@rocket.chat/apps-engine': patch +'@rocket.chat/models': patch +'@rocket.chat/i18n': patch +'@rocket.chat/meteor': patch +--- + +Fix an issue with apps installations via Marketplace diff --git a/apps/meteor/ee/app/license/server/canEnableApp.ts b/apps/meteor/ee/app/license/server/canEnableApp.ts index c4ad4d5bcf79..c18ad9efb661 100644 --- a/apps/meteor/ee/app/license/server/canEnableApp.ts +++ b/apps/meteor/ee/app/license/server/canEnableApp.ts @@ -38,7 +38,8 @@ export const _canEnableApp = async ({ Apps, License }: _canEnableAppDependencies throw new Error('license-prevented'); } - if (app.marketplaceInfo?.isEnterpriseOnly && !License.hasValidLicense()) { + const marketplaceInfo = app.marketplaceInfo?.[0]; + if (marketplaceInfo?.isEnterpriseOnly && !License.hasValidLicense()) { throw new Error('invalid-license'); } diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index 9f18d1353d4e..7611172880a3 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -1,6 +1,7 @@ import { AppStatus, AppStatusUtils } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; import type { AppManager } from '@rocket.chat/apps-engine/server/AppManager'; +import type { IMarketplaceInfo } from '@rocket.chat/apps-engine/server/marketplace'; import type { IUser, IMessage } from '@rocket.chat/core-typings'; import { License } from '@rocket.chat/license'; import { Settings, Users } from '@rocket.chat/models'; @@ -314,7 +315,7 @@ export class AppsRestApi { }, async post() { let buff; - let marketplaceInfo; + let marketplaceInfo: IMarketplaceInfo[] | undefined; let permissionsGranted; if (this.bodyParams.url) { @@ -357,7 +358,15 @@ export class AppsRestApi { } buff = Buffer.from(await downloadResponse.arrayBuffer()); - marketplaceInfo = (await marketplaceResponse.json()) as any; + marketplaceInfo = await marketplaceResponse.json(); + + // Note: marketplace responds with an array of the marketplace info on the app, but it is expected + // to always have one element since we are fetching a specific app version. + if (!Array.isArray(marketplaceInfo) || marketplaceInfo?.length !== 1) { + orchestrator.getRocketChatLogger().error('Error getting the App information from the Marketplace:', marketplaceInfo); + throw new Error('Invalid response from the Marketplace'); + } + permissionsGranted = this.bodyParams.permissionsGranted; } catch (err: any) { return API.v1.failure(err.message); diff --git a/apps/meteor/ee/server/apps/marketplace/appInfo.ts b/apps/meteor/ee/server/apps/marketplace/appInfo.ts deleted file mode 100644 index d2cc23ee9dcf..000000000000 --- a/apps/meteor/ee/server/apps/marketplace/appInfo.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { IMarketplaceInfo } from '@rocket.chat/apps-engine/server/marketplace'; -import { serverFetch as fetch } from '@rocket.chat/server-fetch'; - -export const getMarketplaceAppInfo = async ({ - baseUrl, - headers, - appId, - version, -}: { - baseUrl: string; - headers: Record; - appId: string; - version: string; -}): Promise => { - const appInfosURL = new URL(`${baseUrl}/v1/apps/${appId}`); - appInfosURL.searchParams.set('appVersion', String(version)); - const appInfoResponse = await fetch(appInfosURL.toString(), { - headers, - }); - - if (!appInfoResponse.ok) { - const result = await appInfoResponse.json(); - throw new Error(result?.error || 'Error fetching app information from the Marketplace.'); - } - - const [data] = await appInfoResponse.json(); - return data; -}; diff --git a/apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts b/apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts index 8e5b2c0bc3ab..0cb68734fb46 100644 --- a/apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts +++ b/apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts @@ -95,7 +95,7 @@ describe('canEnableApp', () => { const app = getDefaultApp(); app.installationSource = AppInstallationSource.MARKETPLACE; - app.marketplaceInfo = { isEnterpriseOnly: true } as IMarketplaceInfo; + app.marketplaceInfo = [{ isEnterpriseOnly: true } as IMarketplaceInfo]; const deps = { Apps: AppsMock, License }; diff --git a/packages/apps-engine/src/server/AppManager.ts b/packages/apps-engine/src/server/AppManager.ts index 0ea7e998e995..bbc31eef5e63 100644 --- a/packages/apps-engine/src/server/AppManager.ts +++ b/packages/apps-engine/src/server/AppManager.ts @@ -39,7 +39,7 @@ import { AppInstallationSource } from './storage/IAppStorageItem'; export interface IAppInstallParameters { enable: boolean; - marketplaceInfo?: IMarketplaceInfo; + marketplaceInfo?: IMarketplaceInfo[]; permissionsGranted?: Array; user: IUser; } @@ -877,13 +877,13 @@ export class AppManager { } const appStorageItem = app.getStorageItem(); - const subscriptionInfo = appStorageItem.marketplaceInfo?.subscriptionInfo; + const { subscriptionInfo } = appStorageItem.marketplaceInfo?.[0] || {}; if (subscriptionInfo && subscriptionInfo.license.license === appInfo.subscriptionInfo.license.license) { return; } - appStorageItem.marketplaceInfo.subscriptionInfo = appInfo.subscriptionInfo; + appStorageItem.marketplaceInfo[0].subscriptionInfo = appInfo.subscriptionInfo; return this.appMetadataStorage.update(appStorageItem); }), diff --git a/packages/apps-engine/src/server/managers/AppLicenseManager.ts b/packages/apps-engine/src/server/managers/AppLicenseManager.ts index 7ac6ea677b78..545f20474570 100644 --- a/packages/apps-engine/src/server/managers/AppLicenseManager.ts +++ b/packages/apps-engine/src/server/managers/AppLicenseManager.ts @@ -21,14 +21,15 @@ export class AppLicenseManager { this.userBridge = this.manager.getBridges().getUserBridge(); } - public async validate(validationResult: AppLicenseValidationResult, appMarketplaceInfo?: IMarketplaceInfo): Promise { - if (!appMarketplaceInfo || appMarketplaceInfo.purchaseType !== MarketplacePurchaseType.PurchaseTypeSubscription) { + public async validate(validationResult: AppLicenseValidationResult, appMarketplaceInfo?: IMarketplaceInfo[]): Promise { + const marketplaceInfo = appMarketplaceInfo?.[0]; + if (!marketplaceInfo || marketplaceInfo.purchaseType !== MarketplacePurchaseType.PurchaseTypeSubscription) { return; } validationResult.setValidated(true); - const encryptedLicense = appMarketplaceInfo.subscriptionInfo.license.license; + const encryptedLicense = marketplaceInfo.subscriptionInfo.license.license; if (!encryptedLicense) { validationResult.addError('license', 'License for app is invalid'); @@ -47,7 +48,7 @@ export class AppLicenseManager { switch (license.version) { case LicenseVersion.v1: - await this.validateV1(appMarketplaceInfo, license, validationResult); + await this.validateV1(marketplaceInfo, license, validationResult); break; } } diff --git a/packages/apps-engine/src/server/storage/IAppStorageItem.ts b/packages/apps-engine/src/server/storage/IAppStorageItem.ts index 01670ecb558a..544ec7a505e4 100644 --- a/packages/apps-engine/src/server/storage/IAppStorageItem.ts +++ b/packages/apps-engine/src/server/storage/IAppStorageItem.ts @@ -19,7 +19,7 @@ export interface IAppStorageItem { languageContent: { [key: string]: object }; settings: { [id: string]: ISetting }; implemented: { [int: string]: boolean }; - marketplaceInfo?: IMarketplaceInfo; + marketplaceInfo?: IMarketplaceInfo[]; permissionsGranted?: Array; signature?: string; migrated?: boolean;