fix: Wrong marketplaceInfo value being stored in the database (#35009)

Co-authored-by: Douglas Gubert <1810309+d-gubert@users.noreply.github.com>
pull/34974/head
Gustavo Reis Bauer 11 months ago committed by Guilherme Gazzo
parent 561c3dd648
commit c0fa1c884c
  1. 10
      .changeset/grumpy-lemons-pull.md
  2. 3
      apps/meteor/ee/app/license/server/canEnableApp.ts
  3. 13
      apps/meteor/ee/server/apps/communication/rest.ts
  4. 28
      apps/meteor/ee/server/apps/marketplace/appInfo.ts
  5. 2
      apps/meteor/tests/unit/app/license/server/canEnableApp.spec.ts
  6. 6
      packages/apps-engine/src/server/AppManager.ts
  7. 9
      packages/apps-engine/src/server/managers/AppLicenseManager.ts
  8. 2
      packages/apps-engine/src/server/storage/IAppStorageItem.ts

@ -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

@ -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');
}

@ -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);

@ -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<string, any>;
appId: string;
version: string;
}): Promise<IMarketplaceInfo> => {
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;
};

@ -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 };

@ -39,7 +39,7 @@ import { AppInstallationSource } from './storage/IAppStorageItem';
export interface IAppInstallParameters {
enable: boolean;
marketplaceInfo?: IMarketplaceInfo;
marketplaceInfo?: IMarketplaceInfo[];
permissionsGranted?: Array<IPermission>;
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);
}),

@ -21,14 +21,15 @@ export class AppLicenseManager {
this.userBridge = this.manager.getBridges().getUserBridge();
}
public async validate(validationResult: AppLicenseValidationResult, appMarketplaceInfo?: IMarketplaceInfo): Promise<void> {
if (!appMarketplaceInfo || appMarketplaceInfo.purchaseType !== MarketplacePurchaseType.PurchaseTypeSubscription) {
public async validate(validationResult: AppLicenseValidationResult, appMarketplaceInfo?: IMarketplaceInfo[]): Promise<void> {
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;
}
}

@ -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<IPermission>;
signature?: string;
migrated?: boolean;

Loading…
Cancel
Save