diff --git a/apps/meteor/app/api/server/v1/cloud.ts b/apps/meteor/app/api/server/v1/cloud.ts index 00875f8d21c..507495b150d 100644 --- a/apps/meteor/app/api/server/v1/cloud.ts +++ b/apps/meteor/app/api/server/v1/cloud.ts @@ -7,7 +7,7 @@ import { getCheckoutUrl } from '../../../cloud/server/functions/getCheckoutUrl'; import { getConfirmationPoll } from '../../../cloud/server/functions/getConfirmationPoll'; import { registerPreIntentWorkspaceWizard } from '../../../cloud/server/functions/registerPreIntentWorkspaceWizard'; import { retrieveRegistrationStatus } from '../../../cloud/server/functions/retrieveRegistrationStatus'; -import { saveRegistrationDataManual } from '../../../cloud/server/functions/saveRegistrationData'; +import { saveRegistrationData, saveRegistrationDataManual } from '../../../cloud/server/functions/saveRegistrationData'; import { startRegisterWorkspaceSetupWizard } from '../../../cloud/server/functions/startRegisterWorkspaceSetupWizard'; import { syncWorkspace } from '../../../cloud/server/functions/syncWorkspace'; import { API } from '../api'; @@ -104,7 +104,7 @@ API.v1.addRoute( const pollData = await getConfirmationPoll(deviceCode); if (pollData) { if ('successful' in pollData && pollData.successful) { - await saveRegistrationDataManual(pollData.payload); + await saveRegistrationData(pollData.payload); } return API.v1.success({ pollData }); } diff --git a/ee/packages/license/__tests__/MockedLicenseBuilder.ts b/ee/packages/license/__tests__/MockedLicenseBuilder.ts index d68976b23df..dd2698b2681 100644 --- a/ee/packages/license/__tests__/MockedLicenseBuilder.ts +++ b/ee/packages/license/__tests__/MockedLicenseBuilder.ts @@ -110,6 +110,48 @@ export class MockedLicenseBuilder { }; } + public withExpiredDate(): this { + // expired 1 minute ago + const date = new Date(); + date.setMinutes(date.getMinutes() - 1); + const expired = date.toISOString(); + + const rule = this.validation.validPeriods.find((period) => period.invalidBehavior === 'invalidate_license'); + + if (rule) { + rule.validUntil = expired; + } else { + this.validation.validPeriods.push({ + invalidBehavior: 'invalidate_license', + validFrom: new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString(), + validUntil: expired, + }); + } + + return this; + } + + public withNotStartedDate(): this { + // starts in 1 minute + const date = new Date(); + date.setMinutes(date.getMinutes() + 1); + const starts = date.toISOString(); + + const rule = this.validation.validPeriods.find((period) => period.invalidBehavior === 'invalidate_license'); + + if (rule) { + rule.validFrom = starts; + } else { + this.validation.validPeriods.push({ + invalidBehavior: 'invalidate_license', + validUntil: new Date(new Date().setFullYear(new Date().getFullYear() + 1)).toISOString(), + validFrom: starts, + }); + } + + return this; + } + public resetValidPeriods(): this { this.validation.validPeriods = []; return this; diff --git a/ee/packages/license/__tests__/setLicense.spec.ts b/ee/packages/license/__tests__/setLicense.spec.ts index 35a7a495edc..f077a4fd0e0 100644 --- a/ee/packages/license/__tests__/setLicense.spec.ts +++ b/ee/packages/license/__tests__/setLicense.spec.ts @@ -32,6 +32,57 @@ describe('License set license procedures', () => { }); }); + describe('Invalid periods', () => { + it('should throw an error if the license is expired', async () => { + const license = await getReadyLicenseManager(); + + const mocked = await new MockedLicenseBuilder(); + const token = await mocked.withExpiredDate().sign(); + + await license.setLicense(token); + await expect(license.hasValidLicense()).toBe(false); + }); + + describe('license that is not not started yet is applied', () => { + it('should throw an error if the license is not started yet', async () => { + const license = await getReadyLicenseManager(); + + const mocked = new MockedLicenseBuilder(); + const token = await mocked.withNotStartedDate().sign(); + + await license.setLicense(token); + await expect(license.hasValidLicense()).toBe(false); + }); + + it('should be allowed to set the same license again if the license is not started yet', async () => { + const license = await getReadyLicenseManager(); + + const mocked = await new MockedLicenseBuilder(); + const as = await mocked.resetValidPeriods().withNotStartedDate(); + const token = await as.sign(); + + await license.setLicense(token); + + await expect(license.hasValidLicense()).toBe(false); + + // 5 minutes in the future + + const mockedData = new Date(); + + mockedData.setMinutes(mockedData.getMinutes() + 5); + + jest.useFakeTimers(); + jest.setSystemTime(mockedData); + + await license.setLicense(token); + + jest.useRealTimers(); + + await expect(license.hasValidLicense()).toBe(true); + }); + }); + }); + it('should throw an error if the license is duplicated', async () => { const license = await getReadyLicenseManager(); diff --git a/ee/packages/license/src/license.ts b/ee/packages/license/src/license.ts index c8a5c0a514d..7e15296de56 100644 --- a/ee/packages/license/src/license.ts +++ b/ee/packages/license/src/license.ts @@ -163,7 +163,6 @@ export class LicenseManager extends Emitter { this._license = newLicense; this._lockedLicense = encryptedLicense; - await this.validateLicense({ isNewLicense }); } catch (e) { if (e instanceof InvalidLicenseError) { @@ -251,10 +250,16 @@ export class LicenseManager extends Emitter { if (hasPendingLicense.call(this) && !isPendingLicense.call(this, encryptedLicense)) { // simply remove the pending license clearPendingLicense.call(this); - throw new Error('Invalid license 1'); + throw new Error('Invalid license'); } - throw new DuplicatedLicenseError(); + /** + * The license can be set with future minimum date, failing during the first set, + * but if the user tries to set the same license again later it can be valid or not, so we need to check it again + */ + if (this.hasValidLicense()) { + throw new DuplicatedLicenseError(); + } } if (!isReadyForValidation.call(this)) {