diff --git a/packages/x-license/src/Watermark/Watermark.tsx b/packages/x-license/src/Watermark/Watermark.tsx index b970d5a12fc4..ff1eafba9298 100644 --- a/packages/x-license/src/Watermark/Watermark.tsx +++ b/packages/x-license/src/Watermark/Watermark.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; -import { MuiCommercialPackageName, useLicenseVerifier } from '../useLicenseVerifier'; +import { useLicenseVerifier } from '../useLicenseVerifier'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; +import { MuiCommercialPackageName } from '../utils/commercialPackages'; function getLicenseErrorMessage(licenseStatus: LicenseStatus) { switch (licenseStatus) { diff --git a/packages/x-license/src/useLicenseVerifier/index.ts b/packages/x-license/src/useLicenseVerifier/index.ts index e8e4693717d8..ef9135804f69 100644 --- a/packages/x-license/src/useLicenseVerifier/index.ts +++ b/packages/x-license/src/useLicenseVerifier/index.ts @@ -1,2 +1 @@ export { useLicenseVerifier } from './useLicenseVerifier'; -export type { MuiCommercialPackageName } from './useLicenseVerifier'; diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index fcd64621474d..72fb2bf48f10 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -11,15 +11,8 @@ import { showNotAvailableInInitialProPlanError, } from '../utils/licenseErrorMessageUtils'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { extractAcceptedScopes, extractProductLine } from '../utils/licenseScope'; import MuiLicenseInfoContext from '../Unstable_LicenseInfoProvider/MuiLicenseInfoContext'; - -export type MuiCommercialPackageName = - | 'x-data-grid-pro' - | 'x-data-grid-premium' - | 'x-date-pickers-pro' - | 'x-tree-view-pro' - | 'x-charts-pro'; +import { MuiCommercialPackageName } from '../utils/commercialPackages'; export const sharedLicenseStatuses: { [packageName in MuiCommercialPackageName]?: { @@ -48,15 +41,11 @@ export function useLicenseVerifier( return sharedLicenseStatuses[packageName]!.licenseVerifier; } - const acceptedScopes = extractAcceptedScopes(packageName); - const productLine = extractProductLine(packageName); - const plan = packageName.includes('premium') ? 'Premium' : 'Pro'; const licenseStatus = verifyLicense({ releaseInfo, licenseKey, - acceptedScopes, - productLine, + packageName, }); const fullPackageName = `@mui/${packageName}`; diff --git a/packages/x-license/src/utils/commercialPackages.ts b/packages/x-license/src/utils/commercialPackages.ts new file mode 100644 index 000000000000..ae4e676b5732 --- /dev/null +++ b/packages/x-license/src/utils/commercialPackages.ts @@ -0,0 +1,6 @@ +export type MuiCommercialPackageName = + | 'x-data-grid-pro' + | 'x-data-grid-premium' + | 'x-date-pickers-pro' + | 'x-tree-view-pro' + | 'x-charts-pro'; diff --git a/packages/x-license/src/utils/index.ts b/packages/x-license/src/utils/index.ts index 20a11f9c8cd8..7197239c1e95 100644 --- a/packages/x-license/src/utils/index.ts +++ b/packages/x-license/src/utils/index.ts @@ -3,3 +3,4 @@ export * from './licenseInfo'; export * from './licenseStatus'; export type { LicenseScope } from './licenseScope'; export type { LicensingModel } from './licensingModel'; +export type { MuiCommercialPackageName } from './commercialPackages'; diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index e295cc00f4ed..f998b354f7b0 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -3,17 +3,3 @@ export const PLAN_VERSIONS = ['initial', 'Q3-2024'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; export type PlanVersion = (typeof PLAN_VERSIONS)[number]; -export type ProductLine = 'data-grid' | 'date-pickers' | 'charts' | 'tree-view' - -export const extractProductLine = (packageName: string): ProductLine => { - // extract the part between "x-" and "-pro"/"-premium" - const regex = /x-(.*?)(-pro|-premium)?$/; - const match = packageName.match(regex); - return match![1] as ProductLine; -}; - -export const extractAcceptedScopes = (packageName: string): readonly LicenseScope[] => { - return packageName.includes('premium') - ? LICENSE_SCOPES.filter((scope) => scope.includes('premium')) - : LICENSE_SCOPES; -}; diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index 8b2303b63256..029e78fa6e2d 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -30,8 +30,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: '__RELEASE_INFO__', licenseKey, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.throw('MUI X: The release information is invalid. Not able to validate license.'); }); @@ -42,8 +41,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -62,8 +60,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.ExpiredVersion); }); @@ -75,8 +72,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: 'b43ff5f9ac93f021855ff59ff0ba5220TkFNRTpNYC1VSSBTQVMsREVWRUxPUEVSX0NPVU5UPTEwLEVYUElSWT0xNTkxNzIzMDY3MDQyLFZFUlNJT049MS4yLjM', - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.Invalid); }); @@ -106,8 +102,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: '__RELEASE_INFO__', licenseKey: licenseKeyPro, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.throw('MUI X: The release information is invalid. Not able to validate license.'); }); @@ -119,8 +114,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPro, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -131,8 +125,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPremium, - acceptedScopes: ['premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-premium', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -143,8 +136,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPro, - acceptedScopes: ['premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-premium', }).status, ).to.equal(LICENSE_STATUS.OutOfScope); }); @@ -165,8 +157,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -184,8 +175,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.ExpiredAnnualGrace); }); @@ -204,8 +194,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.ExpiredAnnual); }); @@ -223,8 +212,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -237,8 +225,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: 'b43ff5f9ac93f021855ff59ff0ba5220TkFNRTpNYC1VSSBTQVMsREVWRUxPUEVSX0NPVU5UPTEwLEVYUElSWT0xNTkxNzIzMDY3MDQyLFZFUlNJT049MS4yLjM', - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.Invalid); }); @@ -259,8 +246,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPro, - acceptedScopes: ['pro', 'premium'], - productLine: 'data-grid', + packageName: 'x-data-grid-pro', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -289,8 +275,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKeyInitial, - acceptedScopes: ['pro', 'premium'], - productLine: 'charts', + packageName: 'x-charts-pro', }).status, ).to.equal(LICENSE_STATUS.NotAvailableInInitialProPlan); }); @@ -301,8 +286,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKeyInitial, - acceptedScopes: ['pro', 'premium'], - productLine: 'tree-view', + packageName: 'x-tree-view-pro', }).status, ).to.equal(LICENSE_STATUS.NotAvailableInInitialProPlan); }); @@ -313,8 +297,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKey2, - acceptedScopes: ['pro', 'premium'], - productLine: 'charts', + packageName: 'x-charts-pro', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -325,8 +308,7 @@ describe('License: verifyLicense', () => { verifyLicense({ releaseInfo: RELEASE_INFO, licenseKey: licenseKey2, - acceptedScopes: ['pro', 'premium'], - productLine: 'tree-view', + packageName: 'x-tree-view-pro', }).status, ).to.equal(LICENSE_STATUS.Valid); }); diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index 4515a9aa48d4..ffdcbb8279ed 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -1,8 +1,9 @@ import { base64Decode, base64Encode } from '../encoding/base64'; import { md5 } from '../encoding/md5'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { LicenseScope, LICENSE_SCOPES, ProductLine, PlanVersion } from '../utils/licenseScope'; +import { LicenseScope, LICENSE_SCOPES, PlanVersion } from '../utils/licenseScope'; import { LicensingModel, LICENSING_MODELS } from '../utils/licensingModel'; +import { MuiCommercialPackageName } from '../utils/commercialPackages'; const getDefaultReleaseDate = () => { const today = new Date(); @@ -15,6 +16,22 @@ export function generateReleaseInfo(releaseDate = getDefaultReleaseDate()) { return base64Encode(releaseDate.getTime().toString()); } +function isLicenseScopeSufficient( + packageName: MuiCommercialPackageName, + licenseScope: LicenseScope, +) { + let acceptedScopes: LicenseScope[]; + if (packageName.includes('pro')) { + acceptedScopes = ['pro', 'premium']; + } else if (packageName.includes('premium')) { + acceptedScopes = ['premium']; + } else { + acceptedScopes = []; + } + + return acceptedScopes.includes(licenseScope); +} + const expiryReg = /^.*EXPIRY=([0-9]+),.*$/; interface MuiLicense { @@ -24,7 +41,10 @@ interface MuiLicense { planVersion: PlanVersion; } -const PRODUCT_LINES_AVAILABLE_IN_INITIAL_PRO_PLAN: ProductLine[] = ['data-grid', 'date-pickers'] +const PRO_PACKAGES_AVAILABLE_IN_INITIAL_PRO_PLAN: MuiCommercialPackageName[] = [ + 'x-data-grid-pro', + 'x-date-pickers-pro', +]; /** * Format: ORDER:${orderNumber},EXPIRY=${expiryTimestamp},KEYVERSION=1 @@ -107,13 +127,11 @@ const decodeLicense = (encodedLicense: string): MuiLicense | null => { export function verifyLicense({ releaseInfo, licenseKey, - acceptedScopes, - productLine, + packageName, }: { releaseInfo: string; licenseKey?: string; - acceptedScopes: readonly LicenseScope[]; - productLine: ProductLine; + packageName: MuiCommercialPackageName; }): { status: LicenseStatus; meta?: any } { if (!releaseInfo) { throw new Error('MUI X: The release information is missing. Not able to validate license.'); @@ -180,13 +198,17 @@ export function verifyLicense({ return { status: LICENSE_STATUS.Invalid }; } - // 'charts-pro' or 'tree-view-pro' can only be used with a newer Pro license - if (license.planVersion === 'initial' && license.scope === 'pro' && !PRODUCT_LINES_AVAILABLE_IN_INITIAL_PRO_PLAN.includes(productLine)) { - return { status: LICENSE_STATUS.NotAvailableInInitialProPlan }; + if (!isLicenseScopeSufficient(packageName, license.scope)) { + return { status: LICENSE_STATUS.OutOfScope }; } - if (!acceptedScopes.includes(license.scope)) { - return { status: LICENSE_STATUS.OutOfScope }; + // 'charts-pro' or 'tree-view-pro' can only be used with a newer Pro license + if ( + license.planVersion === 'initial' && + license.scope === 'pro' && + !PRO_PACKAGES_AVAILABLE_IN_INITIAL_PRO_PLAN.includes(packageName) + ) { + return { status: LICENSE_STATUS.NotAvailableInInitialProPlan }; } return { status: LICENSE_STATUS.Valid };