From d3de8a14375a2c512700b93ac0ba44606c1f01b7 Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 08:14:54 -0300 Subject: [PATCH 01/19] feat: Add new IWorkspaceCredential type This type will represent our clound credentials along with their expiration tokens --- packages/core-typings/src/ee/IWorkspaceCredentials.ts | 8 ++++++++ packages/core-typings/src/index.ts | 1 + 2 files changed, 9 insertions(+) create mode 100644 packages/core-typings/src/ee/IWorkspaceCredentials.ts diff --git a/packages/core-typings/src/ee/IWorkspaceCredentials.ts b/packages/core-typings/src/ee/IWorkspaceCredentials.ts new file mode 100644 index 000000000000..1acf4570f3cf --- /dev/null +++ b/packages/core-typings/src/ee/IWorkspaceCredentials.ts @@ -0,0 +1,8 @@ +import type { IRocketChatRecord } from '../IRocketChatRecord'; + +export interface IWorkspaceCredentials extends IRocketChatRecord { + _id: string; + scopes: string[]; + expirationDate: Date; + accessToken: string; +} diff --git a/packages/core-typings/src/index.ts b/packages/core-typings/src/index.ts index 5d2e2935a466..56db4cd73b1f 100644 --- a/packages/core-typings/src/index.ts +++ b/packages/core-typings/src/index.ts @@ -42,6 +42,7 @@ export * from './IUserStatus'; export * from './IUser'; export * from './ee/IAuditLog'; +export * from './ee/IWorkspaceCredentials'; export * from './import'; export * from './IIncomingMessage'; From 7a8dfeaea9b7e23c0125b218f8e1ff8b287ffa73 Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 08:16:00 -0300 Subject: [PATCH 02/19] feat: Add model definition for the IWorkspaceCredential model Here we will have a few operations into a WorkspaceCredential, such as creating, deleting, updating, etc --- packages/model-typings/src/index.ts | 1 + .../src/models/IWorkspaceCredentialsModel.ts | 11 +++++++++++ packages/models/src/index.ts | 2 ++ 3 files changed, 14 insertions(+) create mode 100644 packages/model-typings/src/models/IWorkspaceCredentialsModel.ts diff --git a/packages/model-typings/src/index.ts b/packages/model-typings/src/index.ts index 83def2bd19b2..77fe8f012ec9 100644 --- a/packages/model-typings/src/index.ts +++ b/packages/model-typings/src/index.ts @@ -81,3 +81,4 @@ export * from './models/ICronHistoryModel'; export * from './models/IMigrationsModel'; export * from './models/IModerationReportsModel'; export * from './updater'; +export * from './models/IWorkspaceCredentialsModel'; diff --git a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts new file mode 100644 index 000000000000..0e4dd9b133f2 --- /dev/null +++ b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts @@ -0,0 +1,11 @@ + +import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; + +import type { IBaseModel } from './IBaseModel'; + +export interface IWorkspaceCredentialsModel extends IBaseModel { + getCredentialByScope(scope: string): Promise; + unsetCredentialByScope(scope: string): Promise; + updateCredentialByScope(scope: string, accessToken: string, expirationDate: Date): Promise; + removeAllCredentials(): Promise; +} diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index eb357ed293ef..67bb4dfbcd47 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -79,6 +79,7 @@ import type { ICronHistoryModel, IMigrationsModel, IModerationReportsModel, + IWorkspaceCredentialsModel, } from '@rocket.chat/model-typings'; import { proxify } from './proxify'; @@ -173,3 +174,4 @@ export const AuditLog = proxify('IAuditLogModel'); export const CronHistory = proxify('ICronHistoryModel'); export const Migrations = proxify('IMigrationsModel'); export const ModerationReports = proxify('IModerationReportsModel'); +export const WorkspaceCredentials = proxify('IWorkspaceCredentialsModel'); From d39010292480e61513a937a7338fb9f6713ea5dc Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 08:16:24 -0300 Subject: [PATCH 03/19] chore: Removing the old setting from the setup-wizard --- apps/meteor/server/settings/setup-wizard.ts | 22 --------------------- 1 file changed, 22 deletions(-) diff --git a/apps/meteor/server/settings/setup-wizard.ts b/apps/meteor/server/settings/setup-wizard.ts index 25b2fab19cbd..91e3125d1280 100644 --- a/apps/meteor/server/settings/setup-wizard.ts +++ b/apps/meteor/server/settings/setup-wizard.ts @@ -1322,28 +1322,6 @@ export const createSetupWSettings = () => secret: true, }); - await this.add('Cloud_Workspace_Access_Token', '', { - type: 'string', - hidden: true, - readonly: true, - enableQuery: { - _id: 'Register_Server', - value: true, - }, - secret: true, - }); - - await this.add('Cloud_Workspace_Access_Token_Expires_At', new Date(0), { - type: 'date', - hidden: true, - readonly: true, - enableQuery: { - _id: 'Register_Server', - value: true, - }, - secret: true, - }); - await this.add('Cloud_Workspace_Registration_State', '', { type: 'string', hidden: true, From 12d859d2b8339cb0a6e481e8ba03729fa1132283 Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 08:17:06 -0300 Subject: [PATCH 04/19] feat: Adding a migration to update the new access token collection from the old setttings --- .../ee/server/models/WorkspaceCredentials.ts | 7 +++ .../server/models/raw/WorkspaceCredentials.ts | 60 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 apps/meteor/ee/server/models/WorkspaceCredentials.ts create mode 100644 apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts diff --git a/apps/meteor/ee/server/models/WorkspaceCredentials.ts b/apps/meteor/ee/server/models/WorkspaceCredentials.ts new file mode 100644 index 000000000000..d4f5cd2d912d --- /dev/null +++ b/apps/meteor/ee/server/models/WorkspaceCredentials.ts @@ -0,0 +1,7 @@ +import { registerModel } from '@rocket.chat/models'; + +import { trashCollection } from '../../../server/database/trash'; +import { db } from '../../../server/database/utils'; +import { WorkspaceCredentialsRaw } from './raw/WorkspaceCredentials'; + +registerModel('IWorkspaceCredentialsModel', new WorkspaceCredentialsRaw(db, trashCollection)); diff --git a/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts b/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts new file mode 100644 index 000000000000..b050ff89a97f --- /dev/null +++ b/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts @@ -0,0 +1,60 @@ +import type { RocketChatRecordDeleted, IWorkspaceCredentials } from '@rocket.chat/core-typings'; +import type { IWorkspaceCredentialsModel } from '@rocket.chat/model-typings'; +import type { Collection, Db, Filter, IndexDescription } from 'mongodb'; + +import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; + +export class WorkspaceCredentialsRaw extends BaseRaw implements IWorkspaceCredentialsModel { + constructor(db: Db, trash?: Collection>) { + super(db, 'workspace_credentials', trash); + } + + protected modelIndexes(): IndexDescription[] { + return [{ key: { _id: 1, scopes: 1, expirationDate: 1, accessToken: 1 }, unique: true }]; + } + + getCredentialByScope(scope: string): Promise { + const query: Filter = { + scopes: { + $all: [scope], + $size: 1, + }, + }; + + return this.findOne(query); + } + + async unsetCredentialByScope(scope: string): Promise { + const query: Filter = { + scopes: { + $all: [scope], + $size: 1, + }, + }; + + await this.deleteOne(query); + } + + async updateCredentialByScope(scope: string, accessToken: string, expirationDate: Date): Promise { + const record = { + $set: { + scopes: [scope], + accessToken, + expirationDate, + }, + }; + + const query: Filter = { + scopes: { + $all: [scope], + $size: 1, + }, + }; + + await this.updateOne(query, record, { upsert: true }); + } + + async removeAllCredentials(): Promise { + await this.col.deleteMany({}); + } +} From cb0cb6926a0c8861661f3b4b5b0119a1ee1b697a Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 08:17:34 -0300 Subject: [PATCH 05/19] feat: Using the new collection to store cloud workspace access tokens --- apps/meteor/app/api/server/v1/misc.ts | 9 ++-- .../functions/getWorkspaceAccessToken.ts | 37 ++++++------- .../cloud/server/functions/getWorkspaceKey.ts | 4 +- .../server/functions/getWorkspaceLicense.ts | 3 +- .../cloud/server/functions/removeLicense.ts | 4 +- .../removeWorkspaceRegistrationInfo.ts | 7 ++- .../functions/retrieveRegistrationStatus.ts | 6 ++- .../server/functions/saveRegistrationData.ts | 53 ++++++++----------- .../syncWorkspace/legacySyncWorkspace.ts | 3 +- .../functions/syncWorkspace/syncCloudData.ts | 4 +- .../ee/server/apps/communication/rest.ts | 31 ++++++++--- apps/meteor/ee/server/models/startup.ts | 1 + 12 files changed, 88 insertions(+), 74 deletions(-) diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index 349dbe824bb2..cc5f9e5b6ad8 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -1,7 +1,7 @@ import crypto from 'crypto'; import { isOAuthUser, type IUser } from '@rocket.chat/core-typings'; -import { Settings, Users } from '@rocket.chat/models'; +import { Settings, Users, WorkspaceCredentials } from '@rocket.chat/models'; import { isShieldSvgProps, isSpotlightProps, @@ -675,9 +675,8 @@ API.v1.addRoute( 'Cloud_Workspace_PublicKey', 'Cloud_Workspace_License', 'Cloud_Workspace_Had_Trial', - 'Cloud_Workspace_Access_Token', 'uniqueID', - 'Cloud_Workspace_Access_Token_Expires_At', + 'unsetCredentials', ); } @@ -696,6 +695,10 @@ API.v1.addRoute( return Settings.updateValueById('Deployment_FingerPrint_Verified', true); } + if (settingId === 'unsetCredentials') { + return WorkspaceCredentials.unsetCredentialByScope(''); + } + return Settings.resetValueById(settingId); }); diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index 1ea20812c062..0808a10d53dc 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -1,15 +1,21 @@ -import { Settings } from '@rocket.chat/models'; +import { WorkspaceCredentials } from '@rocket.chat/models'; -import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; -import { settings } from '../../../settings/server'; import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; +import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; + +const hasWorkspaceAccessTokenExpired = (credentials: IWorkspaceCredentials): boolean => new Date() >= credentials.expirationDate; /** - * @param {boolean} forceNew - * @param {string} scope - * @param {boolean} save - * @returns string + * Returns the access token for the workspace, if it is expired or forceNew is true, it will get a new one + * and save it to the database, therefore if this function does not throw an error, it will always return a valid token. + * + * @param {boolean} forceNew - If true, it will get a new token even if the current one is not expired + * @param {string} scope - The scope of the token to get + * @param {boolean} save - If true, it will save the new token to the database + * @throws {CloudWorkspaceAccessTokenError} If the workspace is not registered (no credentials in the database) + * + * @returns string - A valid access token for the workspace */ export async function getWorkspaceAccessToken(forceNew = false, scope = '', save = true, throwOnError = false): Promise { const { workspaceRegistered } = await retrieveRegistrationStatus(); @@ -18,26 +24,21 @@ export async function getWorkspaceAccessToken(forceNew = false, scope = '', save return ''; } - const expires = await Settings.findOneById('Cloud_Workspace_Access_Token_Expires_At'); - - if (expires === null) { - throw new Error('Cloud_Workspace_Access_Token_Expires_At is not set'); + const workspaceCredentials = await WorkspaceCredentials.getCredentialByScope(scope); + if (!workspaceCredentials) { + throw new CloudWorkspaceAccessTokenError(); } const now = new Date(); - if (expires.value && now < expires.value && !forceNew) { - return settings.get('Cloud_Workspace_Access_Token'); + if (!hasWorkspaceAccessTokenExpired(workspaceCredentials) && !forceNew) { + return workspaceCredentials.accessToken; } const accessToken = await getWorkspaceAccessTokenWithScope(scope, throwOnError); if (save) { - (await Settings.updateValueById('Cloud_Workspace_Access_Token', accessToken.token)).modifiedCount && - void notifyOnSettingChangedById('Cloud_Workspace_Access_Token'); - - (await Settings.updateValueById('Cloud_Workspace_Access_Token_Expires_At', accessToken.expiresAt)).modifiedCount && - void notifyOnSettingChangedById('Cloud_Workspace_Access_Token_Expires_At'); + await WorkspaceCredentials.updateCredentialByScope(scope, accessToken.token, accessToken.expiresAt); } return accessToken.token; diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts index 639f29402fe9..36b684b12b7b 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts @@ -1,4 +1,4 @@ -import { settings } from '../../../settings/server'; +import { Settings } from '@rocket.chat/models'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; export async function getWorkspaceKey() { @@ -8,7 +8,7 @@ export async function getWorkspaceKey() { return false; } - const publicKey = settings.get('Cloud_Workspace_PublicKey'); + const publicKey = await Settings.getValueById('Cloud_Workspace_PublicKey'); if (!publicKey) { return false; diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts index f6c62a0ed1ad..6669a08f82c7 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts @@ -7,7 +7,6 @@ import { callbacks } from '../../../../lib/callbacks'; import { CloudWorkspaceConnectionError } from '../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceLicenseError } from '../../../../lib/errors/CloudWorkspaceLicenseError'; import { SystemLogger } from '../../../../server/lib/logger/system'; -import { settings } from '../../../settings/server'; import { LICENSE_VERSION } from '../license'; import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; @@ -23,7 +22,7 @@ const workspaceLicensePayloadSchema = v.object({ const assertWorkspaceLicensePayload = compile(workspaceLicensePayloadSchema); const fetchCloudWorkspaceLicensePayload = async ({ token }: { token: string }): Promise> => { - const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/license`, { headers: { Authorization: `Bearer ${token}`, diff --git a/apps/meteor/app/cloud/server/functions/removeLicense.ts b/apps/meteor/app/cloud/server/functions/removeLicense.ts index b9afe2ddf09a..1b60e59f48a3 100644 --- a/apps/meteor/app/cloud/server/functions/removeLicense.ts +++ b/apps/meteor/app/cloud/server/functions/removeLicense.ts @@ -3,10 +3,10 @@ import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { callbacks } from '../../../../lib/callbacks'; import { CloudWorkspaceConnectionError } from '../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceRegistrationError } from '../../../../lib/errors/CloudWorkspaceRegistrationError'; -import { settings } from '../../../settings/server'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from './getWorkspaceAccessToken'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { syncWorkspace } from './syncWorkspace'; +import { Settings } from '@rocket.chat/models'; export async function removeLicense() { try { @@ -20,7 +20,7 @@ export async function removeLicense() { throw new CloudWorkspaceAccessTokenEmptyError(); } - const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/client/downgrade`, { method: 'POST', headers: { diff --git a/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts b/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts index 45e1738e11e6..05594dae8791 100644 --- a/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts +++ b/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts @@ -1,4 +1,4 @@ -import { Settings } from '@rocket.chat/models'; +import { Settings, WorkspaceCredentials } from '@rocket.chat/models'; import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; @@ -18,6 +18,7 @@ export async function removeWorkspaceRegistrationInfo() { 'Cloud_Workspace_PublicKey', 'Cloud_Workspace_Registration_Client_Uri', 'Show_Setup_Wizard', + 'clearCredentials', ]; const promises = settingsIds.map((settingId) => { @@ -25,6 +26,10 @@ export async function removeWorkspaceRegistrationInfo() { return Settings.updateValueById('Show_Setup_Wizard', 'in_progress'); } + if (settingId === 'clearCredentials') { + return WorkspaceCredentials.removeAllCredentials(); + } + return Settings.resetValueById(settingId, null); }); diff --git a/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts b/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts index 0291534ac637..3ed6d505ee0e 100644 --- a/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts +++ b/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts @@ -1,4 +1,4 @@ -import { Users } from '@rocket.chat/models'; +import { Settings, Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; @@ -9,9 +9,11 @@ export async function retrieveRegistrationStatus(): Promise<{ token: string; email: string; }> { + const workspaceId = ((await Settings.getValueById('Cloud_Workspace_Id')) || '') as string; + const info = { workspaceRegistered: !!settings.get('Cloud_Workspace_Client_Id') && !!settings.get('Cloud_Workspace_Client_Secret'), - workspaceId: settings.get('Cloud_Workspace_Id'), + workspaceId, uniqueId: settings.get('uniqueID'), token: '', email: settings.get('Organization_Email') || '', diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts index d92273302442..f60311f8f8da 100644 --- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts @@ -1,10 +1,23 @@ import { applyLicense } from '@rocket.chat/license'; -import { Settings } from '@rocket.chat/models'; +import { Settings, WorkspaceCredentials } from '@rocket.chat/models'; import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; import { settings } from '../../../settings/server'; import { syncCloudData } from './syncWorkspace/syncCloudData'; + +type SaveRegistrationDataDTO = { + workspaceId: string; + client_name: string; + client_id: string; + client_secret: string; + client_secret_expires_at: number; + publicKey: string; + registration_client_uri: string; +}; + +type ManualSaveRegistrationDataDTO = SaveRegistrationDataDTO & { licenseData: { license: string } }; + export async function saveRegistrationData({ workspaceId, client_name, @@ -13,15 +26,7 @@ export async function saveRegistrationData({ client_secret_expires_at, publicKey, registration_client_uri, -}: { - workspaceId: string; - client_name: string; - client_id: string; - client_secret: string; - client_secret_expires_at: number; - publicKey: string; - registration_client_uri: string; -}) { +}: SaveRegistrationDataDTO) { await saveRegistrationDataBase({ workspaceId, client_name, @@ -43,15 +48,7 @@ async function saveRegistrationDataBase({ client_secret_expires_at, publicKey, registration_client_uri, -}: { - workspaceId: string; - client_name: string; - client_id: string; - client_secret: string; - client_secret_expires_at: number; - publicKey: string; - registration_client_uri: string; -}) { +}: SaveRegistrationDataDTO) { const settingsData = [ { _id: 'Register_Server', value: true }, { _id: 'Cloud_Workspace_Id', value: workspaceId }, @@ -63,7 +60,10 @@ async function saveRegistrationDataBase({ { _id: 'Cloud_Workspace_Registration_Client_Uri', value: registration_client_uri }, ]; - const promises = settingsData.map(({ _id, value }) => Settings.updateValueById(_id, value)); + const promises = [ + ...settingsData.map(({ _id, value }) => Settings.updateValueById(_id, value)), + WorkspaceCredentials.updateCredentialByScope('', '', new Date(0)) + ]; (await Promise.all(promises)).forEach((value, index) => { if (value?.modifiedCount) { @@ -104,18 +104,7 @@ export async function saveRegistrationDataManual({ publicKey, registration_client_uri, licenseData, -}: { - workspaceId: string; - client_name: string; - client_id: string; - client_secret: string; - client_secret_expires_at: number; - publicKey: string; - registration_client_uri: string; - licenseData: { - license: string; - }; -}) { +}: ManualSaveRegistrationDataDTO) { await saveRegistrationDataBase({ workspaceId, client_name, diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts index f2e66bfdf9f7..b2665aaa3ee1 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts @@ -6,7 +6,6 @@ import { v, compile } from 'suretype'; import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError'; import { notifyOnSettingChangedById } from '../../../../lib/server/lib/notifyListener'; -import { settings } from '../../../../settings/server'; import type { WorkspaceRegistrationData } from '../buildRegistrationData'; import { buildWorkspaceRegistrationData } from '../buildRegistrationData'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '../getWorkspaceAccessToken'; @@ -92,7 +91,7 @@ const fetchWorkspaceClientPayload = async ({ token: string; workspaceRegistrationData: WorkspaceRegistrationData; }): Promise | undefined> => { - const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/client`, { method: 'POST', headers: { diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts index fc55fc9e34fd..0b911a0fb01f 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts @@ -8,10 +8,10 @@ import { CloudWorkspaceAccessError } from '../../../../../lib/errors/CloudWorksp import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError'; import { SystemLogger } from '../../../../../server/lib/logger/system'; -import { settings } from '../../../../settings/server'; import { buildWorkspaceRegistrationData } from '../buildRegistrationData'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '../getWorkspaceAccessToken'; import { retrieveRegistrationStatus } from '../retrieveRegistrationStatus'; +import { Settings } from '@rocket.chat/models'; const workspaceSyncPayloadSchema = v.object({ workspaceId: v.string().required(), @@ -28,7 +28,7 @@ const fetchWorkspaceSyncPayload = async ({ token: string; data: Cloud.WorkspaceSyncRequestPayload; }): Promise> => { - const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/sync`, { method: 'POST', headers: { diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index 3b80c37f7990..c22410a3583d 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -93,11 +93,15 @@ export class AppsRestApi { { async get() { const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceId = settings.get('Cloud_Workspace_Id'); + const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); + if (!workspaceIdSetting) { + return API.v1.failure('No workspace id found'); + } + const { action, appId, appVersion } = this.queryParams; return API.v1.success({ - url: `${baseUrl}/apps/${appId}/incompatible/${appVersion}/${action}?workspaceId=${workspaceId}&rocketChatVersion=${rocketChatVersion}`, + url: `${baseUrl}/apps/${appId}/incompatible/${appVersion}/${action}?workspaceId=${workspaceIdSetting.value}&rocketChatVersion=${rocketChatVersion}`, }); }, }, @@ -186,7 +190,10 @@ export class AppsRestApi { async get() { const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceId = settings.get('Cloud_Workspace_Id'); + const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); + if (!workspaceIdSetting) { + return API.v1.failure('No workspace id found'); + } if (!this.queryParams.purchaseType || !purchaseTypes.has(this.queryParams.purchaseType)) { return API.v1.failure({ error: 'Invalid purchase type' }); @@ -204,7 +211,7 @@ export class AppsRestApi { return API.v1.success({ url: `${baseUrl}/apps/${this.queryParams.appId}/${ this.queryParams.purchaseType === 'buy' ? this.queryParams.purchaseType : subscribeRoute - }?workspaceId=${workspaceId}&token=${response.token}&seats=${seats}`, + }?workspaceId=${workspaceIdSetting.value}&token=${response.token}&seats=${seats}`, }); }, }, @@ -286,7 +293,11 @@ export class AppsRestApi { this.queryParams.appId ) { apiDeprecationLogger.endpoint(this.request.route, '7.0.0', this.response, 'Use /apps/buildExternalUrl to get the modal URLs.'); - const workspaceId = settings.get('Cloud_Workspace_Id'); + + const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); + if (!workspaceIdSetting) { + return API.v1.failure('No workspace id found'); + } if (!this.queryParams.purchaseType || !purchaseTypes.has(this.queryParams.purchaseType)) { return API.v1.failure({ error: 'Invalid purchase type' }); @@ -304,7 +315,7 @@ export class AppsRestApi { return API.v1.success({ url: `${baseUrl}/apps/${this.queryParams.appId}/${ this.queryParams.purchaseType === 'buy' ? this.queryParams.purchaseType : subscribeRoute - }?workspaceId=${workspaceId}&token=${token.token}&seats=${seats}`, + }?workspaceId=${workspaceIdSetting.value}&token=${token.token}&seats=${seats}`, }); } apiDeprecationLogger.endpoint(this.request.route, '7.0.0', this.response, 'Use /apps/installed to get the installed apps list.'); @@ -444,8 +455,12 @@ export class AppsRestApi { } const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceId = settings.get('Cloud_Workspace_Id'); + const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); + if (!workspaceIdSetting) { + return API.v1.failure('No workspace id found'); + } + const requester = { id: this.user._id, username: this.user.username, @@ -482,7 +497,7 @@ export class AppsRestApi { } const queryParams = new URLSearchParams(); - queryParams.set('workspaceId', workspaceId); + queryParams.set('workspaceId', workspaceIdSetting.value as string); queryParams.set('frameworkVersion', appsEngineVersionForMarketplace); queryParams.set('requester', Buffer.from(JSON.stringify(requester)).toString('base64')); queryParams.set('admins', Buffer.from(JSON.stringify(admins)).toString('base64')); diff --git a/apps/meteor/ee/server/models/startup.ts b/apps/meteor/ee/server/models/startup.ts index f77bcd1d7619..08605fe2c4d2 100644 --- a/apps/meteor/ee/server/models/startup.ts +++ b/apps/meteor/ee/server/models/startup.ts @@ -7,6 +7,7 @@ import('./LivechatPriority'); import('./OmnichannelServiceLevelAgreements'); import('./AuditLog'); import('./ReadReceipts'); +import('./WorkspaceCredentials'); void License.onLicense('livechat-enterprise', () => { import('./CannedResponse'); From c2c3009107ee323874baaae13fae4ac386a93d85 Mon Sep 17 00:00:00 2001 From: Gustavo Reis Bauer Date: Mon, 22 Jul 2024 08:29:54 -0300 Subject: [PATCH 06/19] feat: Create changeset --- .changeset/plenty-hairs-camp.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/plenty-hairs-camp.md diff --git a/.changeset/plenty-hairs-camp.md b/.changeset/plenty-hairs-camp.md new file mode 100644 index 000000000000..3a6c74b472bf --- /dev/null +++ b/.changeset/plenty-hairs-camp.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-typings": patch +"@rocket.chat/model-typings": patch +"@rocket.chat/models": patch +--- + +Now, we are not fetching cloud credentials from a cache, by doing so, we are avoiding the use of stale cloud credentials when comunicating with cloud services. We are, also, using a new collection to store the credentials and their scopes. From f406fc6d5ebb3b240d9959b041826abf14bb883b Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 09:22:55 -0300 Subject: [PATCH 07/19] fix: lint-fix --- .../app/cloud/server/functions/getWorkspaceAccessToken.ts | 2 -- packages/model-typings/src/models/IWorkspaceCredentialsModel.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index 0808a10d53dc..85e4c41e7e48 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -29,8 +29,6 @@ export async function getWorkspaceAccessToken(forceNew = false, scope = '', save throw new CloudWorkspaceAccessTokenError(); } - const now = new Date(); - if (!hasWorkspaceAccessTokenExpired(workspaceCredentials) && !forceNew) { return workspaceCredentials.accessToken; } diff --git a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts index 0e4dd9b133f2..996b7e8019d6 100644 --- a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts +++ b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts @@ -1,4 +1,3 @@ - import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; import type { IBaseModel } from './IBaseModel'; From 658641906882bcf581d151b1ca90a7f30f208393 Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 10:12:12 -0300 Subject: [PATCH 08/19] fix: Sorry linter --- .../app/cloud/server/functions/getWorkspaceAccessToken.ts | 4 ++-- apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts | 1 + apps/meteor/app/cloud/server/functions/removeLicense.ts | 2 +- .../meteor/app/cloud/server/functions/saveRegistrationData.ts | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index 85e4c41e7e48..3bbea3f04e0b 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -1,8 +1,8 @@ -import { WorkspaceCredentials } from '@rocket.chat/models'; - import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; + import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; +import { WorkspaceCredentials } from '@rocket.chat/models'; const hasWorkspaceAccessTokenExpired = (credentials: IWorkspaceCredentials): boolean => new Date() >= credentials.expirationDate; diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts index 36b684b12b7b..140045f5d9a0 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts @@ -1,4 +1,5 @@ import { Settings } from '@rocket.chat/models'; + import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; export async function getWorkspaceKey() { diff --git a/apps/meteor/app/cloud/server/functions/removeLicense.ts b/apps/meteor/app/cloud/server/functions/removeLicense.ts index 1b60e59f48a3..613f49134282 100644 --- a/apps/meteor/app/cloud/server/functions/removeLicense.ts +++ b/apps/meteor/app/cloud/server/functions/removeLicense.ts @@ -1,3 +1,4 @@ +import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { callbacks } from '../../../../lib/callbacks'; @@ -6,7 +7,6 @@ import { CloudWorkspaceRegistrationError } from '../../../../lib/errors/CloudWor import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from './getWorkspaceAccessToken'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { syncWorkspace } from './syncWorkspace'; -import { Settings } from '@rocket.chat/models'; export async function removeLicense() { try { diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts index f60311f8f8da..1e29eac1f041 100644 --- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts @@ -5,7 +5,6 @@ import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListen import { settings } from '../../../settings/server'; import { syncCloudData } from './syncWorkspace/syncCloudData'; - type SaveRegistrationDataDTO = { workspaceId: string; client_name: string; @@ -62,7 +61,7 @@ async function saveRegistrationDataBase({ const promises = [ ...settingsData.map(({ _id, value }) => Settings.updateValueById(_id, value)), - WorkspaceCredentials.updateCredentialByScope('', '', new Date(0)) + WorkspaceCredentials.updateCredentialByScope('', '', new Date(0)), ]; (await Promise.all(promises)).forEach((value, index) => { From 46d12c19f9df5f4e0f11120633d1e78c55b9d599 Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 10:26:37 -0300 Subject: [PATCH 09/19] fix: Sorry linter2 --- .../app/cloud/server/functions/getWorkspaceAccessToken.ts | 6 +++--- .../cloud/server/functions/syncWorkspace/syncCloudData.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index 3bbea3f04e0b..3ef6493ad359 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -1,9 +1,9 @@ -import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; -import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; - import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; import { WorkspaceCredentials } from '@rocket.chat/models'; +import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; +import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; + const hasWorkspaceAccessTokenExpired = (credentials: IWorkspaceCredentials): boolean => new Date() >= credentials.expirationDate; /** diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts index 0b911a0fb01f..d7722ec1b16a 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts @@ -1,5 +1,6 @@ import type { Cloud, Serialized } from '@rocket.chat/core-typings'; import { DuplicatedLicenseError } from '@rocket.chat/license'; +import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { v, compile } from 'suretype'; @@ -11,7 +12,6 @@ import { SystemLogger } from '../../../../../server/lib/logger/system'; import { buildWorkspaceRegistrationData } from '../buildRegistrationData'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '../getWorkspaceAccessToken'; import { retrieveRegistrationStatus } from '../retrieveRegistrationStatus'; -import { Settings } from '@rocket.chat/models'; const workspaceSyncPayloadSchema = v.object({ workspaceId: v.string().required(), From 3dfe585d7f5ab0040140ca7f6c4abb5d86aa4fc0 Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 22 Jul 2024 10:40:37 -0300 Subject: [PATCH 10/19] fix --- apps/meteor/ee/server/apps/communication/rest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index c22410a3583d..eb647371568b 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -460,7 +460,7 @@ export class AppsRestApi { if (!workspaceIdSetting) { return API.v1.failure('No workspace id found'); } - + const requester = { id: this.user._id, username: this.user.username, From a6c471e28a16d5473e8c5157b098266466c6123a Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 14 Oct 2024 10:34:18 -0300 Subject: [PATCH 11/19] chore: remove settings model call --- .changeset/plenty-hairs-camp.md | 8 +++--- .../cloud/server/functions/getWorkspaceKey.ts | 5 ++-- .../server/functions/getWorkspaceLicense.ts | 3 +- .../cloud/server/functions/removeLicense.ts | 4 +-- .../functions/retrieveRegistrationStatus.ts | 6 ++-- .../syncWorkspace/legacySyncWorkspace.ts | 3 +- .../functions/syncWorkspace/syncCloudData.ts | 4 +-- .../ee/server/apps/communication/rest.ts | 28 ++++++------------- 8 files changed, 24 insertions(+), 37 deletions(-) diff --git a/.changeset/plenty-hairs-camp.md b/.changeset/plenty-hairs-camp.md index 3a6c74b472bf..3e26790fe9fd 100644 --- a/.changeset/plenty-hairs-camp.md +++ b/.changeset/plenty-hairs-camp.md @@ -1,8 +1,8 @@ --- -"@rocket.chat/meteor": patch -"@rocket.chat/core-typings": patch -"@rocket.chat/model-typings": patch -"@rocket.chat/models": patch +"@rocket.chat/meteor": major +"@rocket.chat/core-typings": major +"@rocket.chat/model-typings": major +"@rocket.chat/models": major --- Now, we are not fetching cloud credentials from a cache, by doing so, we are avoiding the use of stale cloud credentials when comunicating with cloud services. We are, also, using a new collection to store the credentials and their scopes. diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts index 140045f5d9a0..639f29402fe9 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceKey.ts @@ -1,5 +1,4 @@ -import { Settings } from '@rocket.chat/models'; - +import { settings } from '../../../settings/server'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; export async function getWorkspaceKey() { @@ -9,7 +8,7 @@ export async function getWorkspaceKey() { return false; } - const publicKey = await Settings.getValueById('Cloud_Workspace_PublicKey'); + const publicKey = settings.get('Cloud_Workspace_PublicKey'); if (!publicKey) { return false; diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts index 6669a08f82c7..f6c62a0ed1ad 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceLicense.ts @@ -7,6 +7,7 @@ import { callbacks } from '../../../../lib/callbacks'; import { CloudWorkspaceConnectionError } from '../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceLicenseError } from '../../../../lib/errors/CloudWorkspaceLicenseError'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { settings } from '../../../settings/server'; import { LICENSE_VERSION } from '../license'; import { getWorkspaceAccessToken } from './getWorkspaceAccessToken'; @@ -22,7 +23,7 @@ const workspaceLicensePayloadSchema = v.object({ const assertWorkspaceLicensePayload = compile(workspaceLicensePayloadSchema); const fetchCloudWorkspaceLicensePayload = async ({ token }: { token: string }): Promise> => { - const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/license`, { headers: { Authorization: `Bearer ${token}`, diff --git a/apps/meteor/app/cloud/server/functions/removeLicense.ts b/apps/meteor/app/cloud/server/functions/removeLicense.ts index 613f49134282..b9afe2ddf09a 100644 --- a/apps/meteor/app/cloud/server/functions/removeLicense.ts +++ b/apps/meteor/app/cloud/server/functions/removeLicense.ts @@ -1,9 +1,9 @@ -import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { callbacks } from '../../../../lib/callbacks'; import { CloudWorkspaceConnectionError } from '../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceRegistrationError } from '../../../../lib/errors/CloudWorkspaceRegistrationError'; +import { settings } from '../../../settings/server'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from './getWorkspaceAccessToken'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; import { syncWorkspace } from './syncWorkspace'; @@ -20,7 +20,7 @@ export async function removeLicense() { throw new CloudWorkspaceAccessTokenEmptyError(); } - const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/client/downgrade`, { method: 'POST', headers: { diff --git a/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts b/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts index 3ed6d505ee0e..0291534ac637 100644 --- a/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts +++ b/apps/meteor/app/cloud/server/functions/retrieveRegistrationStatus.ts @@ -1,4 +1,4 @@ -import { Settings, Users } from '@rocket.chat/models'; +import { Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; @@ -9,11 +9,9 @@ export async function retrieveRegistrationStatus(): Promise<{ token: string; email: string; }> { - const workspaceId = ((await Settings.getValueById('Cloud_Workspace_Id')) || '') as string; - const info = { workspaceRegistered: !!settings.get('Cloud_Workspace_Client_Id') && !!settings.get('Cloud_Workspace_Client_Secret'), - workspaceId, + workspaceId: settings.get('Cloud_Workspace_Id'), uniqueId: settings.get('uniqueID'), token: '', email: settings.get('Organization_Email') || '', diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts index b2665aaa3ee1..f2e66bfdf9f7 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace/legacySyncWorkspace.ts @@ -6,6 +6,7 @@ import { v, compile } from 'suretype'; import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError'; import { notifyOnSettingChangedById } from '../../../../lib/server/lib/notifyListener'; +import { settings } from '../../../../settings/server'; import type { WorkspaceRegistrationData } from '../buildRegistrationData'; import { buildWorkspaceRegistrationData } from '../buildRegistrationData'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '../getWorkspaceAccessToken'; @@ -91,7 +92,7 @@ const fetchWorkspaceClientPayload = async ({ token: string; workspaceRegistrationData: WorkspaceRegistrationData; }): Promise | undefined> => { - const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/client`, { method: 'POST', headers: { diff --git a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts index d7722ec1b16a..fc55fc9e34fd 100644 --- a/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts +++ b/apps/meteor/app/cloud/server/functions/syncWorkspace/syncCloudData.ts @@ -1,6 +1,5 @@ import type { Cloud, Serialized } from '@rocket.chat/core-typings'; import { DuplicatedLicenseError } from '@rocket.chat/license'; -import { Settings } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { v, compile } from 'suretype'; @@ -9,6 +8,7 @@ import { CloudWorkspaceAccessError } from '../../../../../lib/errors/CloudWorksp import { CloudWorkspaceConnectionError } from '../../../../../lib/errors/CloudWorkspaceConnectionError'; import { CloudWorkspaceRegistrationError } from '../../../../../lib/errors/CloudWorkspaceRegistrationError'; import { SystemLogger } from '../../../../../server/lib/logger/system'; +import { settings } from '../../../../settings/server'; import { buildWorkspaceRegistrationData } from '../buildRegistrationData'; import { CloudWorkspaceAccessTokenEmptyError, getWorkspaceAccessToken } from '../getWorkspaceAccessToken'; import { retrieveRegistrationStatus } from '../retrieveRegistrationStatus'; @@ -28,7 +28,7 @@ const fetchWorkspaceSyncPayload = async ({ token: string; data: Cloud.WorkspaceSyncRequestPayload; }): Promise> => { - const workspaceRegistrationClientUri = await Settings.getValueById('Cloud_Workspace_Registration_Client_Uri'); + const workspaceRegistrationClientUri = settings.get('Cloud_Workspace_Registration_Client_Uri'); const response = await fetch(`${workspaceRegistrationClientUri}/sync`, { method: 'POST', headers: { diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index eb647371568b..e63ec9eb2654 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -93,15 +93,12 @@ export class AppsRestApi { { async get() { const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); - if (!workspaceIdSetting) { - return API.v1.failure('No workspace id found'); - } + const workspaceId = settings.get('Cloud_Workspace_Id'); const { action, appId, appVersion } = this.queryParams; return API.v1.success({ - url: `${baseUrl}/apps/${appId}/incompatible/${appVersion}/${action}?workspaceId=${workspaceIdSetting.value}&rocketChatVersion=${rocketChatVersion}`, + url: `${baseUrl}/apps/${appId}/incompatible/${appVersion}/${action}?workspaceId=${workspaceId}&rocketChatVersion=${rocketChatVersion}`, }); }, }, @@ -190,10 +187,7 @@ export class AppsRestApi { async get() { const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); - if (!workspaceIdSetting) { - return API.v1.failure('No workspace id found'); - } + const workspaceId = settings.get('Cloud_Workspace_Id'); if (!this.queryParams.purchaseType || !purchaseTypes.has(this.queryParams.purchaseType)) { return API.v1.failure({ error: 'Invalid purchase type' }); @@ -211,7 +205,7 @@ export class AppsRestApi { return API.v1.success({ url: `${baseUrl}/apps/${this.queryParams.appId}/${ this.queryParams.purchaseType === 'buy' ? this.queryParams.purchaseType : subscribeRoute - }?workspaceId=${workspaceIdSetting.value}&token=${response.token}&seats=${seats}`, + }?workspaceId=${workspaceId}&token=${response.token}&seats=${seats}`, }); }, }, @@ -294,10 +288,7 @@ export class AppsRestApi { ) { apiDeprecationLogger.endpoint(this.request.route, '7.0.0', this.response, 'Use /apps/buildExternalUrl to get the modal URLs.'); - const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); - if (!workspaceIdSetting) { - return API.v1.failure('No workspace id found'); - } + const workspaceId = settings.get('Cloud_Workspace_Id'); if (!this.queryParams.purchaseType || !purchaseTypes.has(this.queryParams.purchaseType)) { return API.v1.failure({ error: 'Invalid purchase type' }); @@ -315,7 +306,7 @@ export class AppsRestApi { return API.v1.success({ url: `${baseUrl}/apps/${this.queryParams.appId}/${ this.queryParams.purchaseType === 'buy' ? this.queryParams.purchaseType : subscribeRoute - }?workspaceId=${workspaceIdSetting.value}&token=${token.token}&seats=${seats}`, + }?workspaceId=${workspaceId}&token=${token.token}&seats=${seats}`, }); } apiDeprecationLogger.endpoint(this.request.route, '7.0.0', this.response, 'Use /apps/installed to get the installed apps list.'); @@ -456,10 +447,7 @@ export class AppsRestApi { const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceIdSetting = await Settings.findOneById('Cloud_Workspace_Id'); - if (!workspaceIdSetting) { - return API.v1.failure('No workspace id found'); - } + const workspaceId = settings.get('Cloud_Workspace_Id'); const requester = { id: this.user._id, @@ -497,7 +485,7 @@ export class AppsRestApi { } const queryParams = new URLSearchParams(); - queryParams.set('workspaceId', workspaceIdSetting.value as string); + queryParams.set('workspaceId', workspaceId as string); queryParams.set('frameworkVersion', appsEngineVersionForMarketplace); queryParams.set('requester', Buffer.from(JSON.stringify(requester)).toString('base64')); queryParams.set('admins', Buffer.from(JSON.stringify(admins)).toString('base64')); From f0fba773e75e5f23897aad34e1eadeb88218abca Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 14 Oct 2024 10:36:21 -0300 Subject: [PATCH 12/19] chore: sorry linter --- apps/meteor/ee/server/apps/communication/rest.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index e63ec9eb2654..512dfd275729 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -93,7 +93,7 @@ export class AppsRestApi { { async get() { const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceId = settings.get('Cloud_Workspace_Id'); + const workspaceId = settings.get('Cloud_Workspace_Id'); const { action, appId, appVersion } = this.queryParams; @@ -287,7 +287,6 @@ export class AppsRestApi { this.queryParams.appId ) { apiDeprecationLogger.endpoint(this.request.route, '7.0.0', this.response, 'Use /apps/buildExternalUrl to get the modal URLs.'); - const workspaceId = settings.get('Cloud_Workspace_Id'); if (!this.queryParams.purchaseType || !purchaseTypes.has(this.queryParams.purchaseType)) { @@ -446,7 +445,6 @@ export class AppsRestApi { } const baseUrl = orchestrator.getMarketplaceUrl(); - const workspaceId = settings.get('Cloud_Workspace_Id'); const requester = { @@ -485,7 +483,7 @@ export class AppsRestApi { } const queryParams = new URLSearchParams(); - queryParams.set('workspaceId', workspaceId as string); + queryParams.set('workspaceId', workspaceId); queryParams.set('frameworkVersion', appsEngineVersionForMarketplace); queryParams.set('requester', Buffer.from(JSON.stringify(requester)).toString('base64')); queryParams.set('admins', Buffer.from(JSON.stringify(admins)).toString('base64')); From 4c160dc28bf77eee312b657e09673ee60881e62d Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 14 Oct 2024 10:37:08 -0300 Subject: [PATCH 13/19] chore: remove extra line --- apps/meteor/ee/server/apps/communication/rest.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index 512dfd275729..3b80c37f7990 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -94,7 +94,6 @@ export class AppsRestApi { async get() { const baseUrl = orchestrator.getMarketplaceUrl(); const workspaceId = settings.get('Cloud_Workspace_Id'); - const { action, appId, appVersion } = this.queryParams; return API.v1.success({ From 0e173f1db361c0c4a9fe3a4a5640b8ece0baeba5 Mon Sep 17 00:00:00 2001 From: gustrb Date: Mon, 14 Oct 2024 10:51:28 -0300 Subject: [PATCH 14/19] chore: use object instead of paramete --- .../functions/getWorkspaceAccessToken.ts | 6 ++++- .../server/functions/saveRegistrationData.ts | 6 ++++- .../server/models/raw/WorkspaceCredentials.ts | 22 +++++++++++++------ .../src/models/IWorkspaceCredentialsModel.ts | 7 +++--- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index 3ef6493ad359..93cfa3266ecf 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -36,7 +36,11 @@ export async function getWorkspaceAccessToken(forceNew = false, scope = '', save const accessToken = await getWorkspaceAccessTokenWithScope(scope, throwOnError); if (save) { - await WorkspaceCredentials.updateCredentialByScope(scope, accessToken.token, accessToken.expiresAt); + await WorkspaceCredentials.updateCredentialByScope({ + scope, + accessToken: accessToken.token, + expirationDate: accessToken.expiresAt, + }); } return accessToken.token; diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts index 1e29eac1f041..2c68b93c628f 100644 --- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts @@ -61,7 +61,11 @@ async function saveRegistrationDataBase({ const promises = [ ...settingsData.map(({ _id, value }) => Settings.updateValueById(_id, value)), - WorkspaceCredentials.updateCredentialByScope('', '', new Date(0)), + WorkspaceCredentials.updateCredentialByScope({ + scope: '', + accessToken: '', + expirationDate: new Date(0), + }), ]; (await Promise.all(promises)).forEach((value, index) => { diff --git a/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts b/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts index b050ff89a97f..814bc365a338 100644 --- a/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts +++ b/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts @@ -1,6 +1,6 @@ import type { RocketChatRecordDeleted, IWorkspaceCredentials } from '@rocket.chat/core-typings'; import type { IWorkspaceCredentialsModel } from '@rocket.chat/model-typings'; -import type { Collection, Db, Filter, IndexDescription } from 'mongodb'; +import type { Collection, Db, DeleteResult, Filter, IndexDescription, UpdateResult } from 'mongodb'; import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; @@ -24,7 +24,7 @@ export class WorkspaceCredentialsRaw extends BaseRaw impl return this.findOne(query); } - async unsetCredentialByScope(scope: string): Promise { + unsetCredentialByScope(scope: string): Promise { const query: Filter = { scopes: { $all: [scope], @@ -32,10 +32,18 @@ export class WorkspaceCredentialsRaw extends BaseRaw impl }, }; - await this.deleteOne(query); + return this.deleteOne(query); } - async updateCredentialByScope(scope: string, accessToken: string, expirationDate: Date): Promise { + updateCredentialByScope({ + scope, + accessToken, + expirationDate, + }: { + scope: string; + accessToken: string; + expirationDate: Date; + }): Promise { const record = { $set: { scopes: [scope], @@ -51,10 +59,10 @@ export class WorkspaceCredentialsRaw extends BaseRaw impl }, }; - await this.updateOne(query, record, { upsert: true }); + return this.updateOne(query, record, { upsert: true }); } - async removeAllCredentials(): Promise { - await this.col.deleteMany({}); + removeAllCredentials(): Promise { + return this.col.deleteMany({}); } } diff --git a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts index 996b7e8019d6..303da0ff1cf9 100644 --- a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts +++ b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts @@ -1,10 +1,11 @@ import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; +import type { DeleteResult, UpdateResult } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; export interface IWorkspaceCredentialsModel extends IBaseModel { getCredentialByScope(scope: string): Promise; - unsetCredentialByScope(scope: string): Promise; - updateCredentialByScope(scope: string, accessToken: string, expirationDate: Date): Promise; - removeAllCredentials(): Promise; + unsetCredentialByScope(scope: string): Promise; + updateCredentialByScope(credentials: { scope: string; accessToken: string; expirationDate: Date }): Promise; + removeAllCredentials(): Promise; } From bff798eb1aa691f7a0b078b465f4b84c6ac5c9f8 Mon Sep 17 00:00:00 2001 From: gustrb Date: Tue, 15 Oct 2024 14:24:35 -0300 Subject: [PATCH 15/19] chore: remove credentials properly we were passing down a weird string to inform when to remove credentials which was causing a type error --- apps/meteor/app/api/server/v1/misc.ts | 6 +----- .../server/functions/removeWorkspaceRegistrationInfo.ts | 7 ++----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index cc5f9e5b6ad8..cf8ece115d36 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -664,6 +664,7 @@ API.v1.addRoute( const settingsIds: string[] = []; if (this.bodyParams.setDeploymentAs === 'new-workspace') { + await WorkspaceCredentials.unsetCredentialByScope(''); settingsIds.push( 'Cloud_Service_Agree_PrivacyTerms', 'Cloud_Workspace_Id', @@ -676,7 +677,6 @@ API.v1.addRoute( 'Cloud_Workspace_License', 'Cloud_Workspace_Had_Trial', 'uniqueID', - 'unsetCredentials', ); } @@ -695,10 +695,6 @@ API.v1.addRoute( return Settings.updateValueById('Deployment_FingerPrint_Verified', true); } - if (settingId === 'unsetCredentials') { - return WorkspaceCredentials.unsetCredentialByScope(''); - } - return Settings.resetValueById(settingId); }); diff --git a/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts b/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts index 05594dae8791..bf2b5d085945 100644 --- a/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts +++ b/apps/meteor/app/cloud/server/functions/removeWorkspaceRegistrationInfo.ts @@ -9,6 +9,8 @@ export async function removeWorkspaceRegistrationInfo() { return true; } + await WorkspaceCredentials.removeAllCredentials(); + const settingsIds = [ 'Cloud_Workspace_Id', 'Cloud_Workspace_Name', @@ -18,7 +20,6 @@ export async function removeWorkspaceRegistrationInfo() { 'Cloud_Workspace_PublicKey', 'Cloud_Workspace_Registration_Client_Uri', 'Show_Setup_Wizard', - 'clearCredentials', ]; const promises = settingsIds.map((settingId) => { @@ -26,10 +27,6 @@ export async function removeWorkspaceRegistrationInfo() { return Settings.updateValueById('Show_Setup_Wizard', 'in_progress'); } - if (settingId === 'clearCredentials') { - return WorkspaceCredentials.removeAllCredentials(); - } - return Settings.resetValueById(settingId, null); }); From 8ce598d2fc817cbdc0ee22814f74c08eeaa05d85 Mon Sep 17 00:00:00 2001 From: gustrb Date: Tue, 15 Oct 2024 14:28:12 -0300 Subject: [PATCH 16/19] chore: removing updateCredentials from promise.all we were assuming a changeset when one was not available --- .../server/functions/saveRegistrationData.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts index 2c68b93c628f..746904687d67 100644 --- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts @@ -59,14 +59,13 @@ async function saveRegistrationDataBase({ { _id: 'Cloud_Workspace_Registration_Client_Uri', value: registration_client_uri }, ]; - const promises = [ - ...settingsData.map(({ _id, value }) => Settings.updateValueById(_id, value)), - WorkspaceCredentials.updateCredentialByScope({ - scope: '', - accessToken: '', - expirationDate: new Date(0), - }), - ]; + await WorkspaceCredentials.updateCredentialByScope({ + scope: '', + accessToken: '', + expirationDate: new Date(0), + }); + + const promises = [...settingsData.map(({ _id, value }) => Settings.updateValueById(_id, value))]; (await Promise.all(promises)).forEach((value, index) => { if (value?.modifiedCount) { From be6eedf68db775cea9ee0da9452251f32fef9cb4 Mon Sep 17 00:00:00 2001 From: gustrb Date: Wed, 16 Oct 2024 14:40:50 -0300 Subject: [PATCH 17/19] chore: fix conflicts --- .../meteor/server/startup/migrations/index.ts | 1 + apps/meteor/server/startup/migrations/v316.ts | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 apps/meteor/server/startup/migrations/v316.ts diff --git a/apps/meteor/server/startup/migrations/index.ts b/apps/meteor/server/startup/migrations/index.ts index bf1cd59dbd0d..aa1e65bca966 100644 --- a/apps/meteor/server/startup/migrations/index.ts +++ b/apps/meteor/server/startup/migrations/index.ts @@ -48,5 +48,6 @@ import './v312'; import './v313'; import './v314'; import './v315'; +import './v316'; export * from './xrun'; diff --git a/apps/meteor/server/startup/migrations/v316.ts b/apps/meteor/server/startup/migrations/v316.ts new file mode 100644 index 000000000000..cdbb3d472385 --- /dev/null +++ b/apps/meteor/server/startup/migrations/v316.ts @@ -0,0 +1,27 @@ +import { Settings, WorkspaceCredentials } from '@rocket.chat/models'; + +import { addMigration } from '../../lib/migrations'; + +addMigration({ + version: 316, + name: 'Remove Cloud_Workspace_Access_Token and Cloud_Workspace_Access_Token_Expires_At from the settings collection and add to the WorkspaceCredentials collection', + async up() { + const workspaceCredentials = await WorkspaceCredentials.getCredentialByScope(''); + if (workspaceCredentials) { + return; + } + + const accessToken = ((await Settings.getValueById('Cloud_Workspace_Access_Token')) as string) || ''; + const accessTokenExpiresAt = ((await Settings.getValueById('Cloud_Workspace_Access_Token_Expires_At')) as Date) || new Date(0); + + if (accessToken) { + await Settings.removeById('Cloud_Workspace_Access_Token'); + } + + if (accessTokenExpiresAt) { + await Settings.removeById('Cloud_Workspace_Access_Token_Expires_At'); + } + + await WorkspaceCredentials.updateCredentialByScope('', accessToken, accessTokenExpiresAt); + }, +}); From a87caa5daa3307af97ac54a2a8f19deff0e71d69 Mon Sep 17 00:00:00 2001 From: gustrb Date: Wed, 16 Oct 2024 15:05:28 -0300 Subject: [PATCH 18/19] chore: call the migration correctly --- apps/meteor/server/startup/migrations/v316.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/meteor/server/startup/migrations/v316.ts b/apps/meteor/server/startup/migrations/v316.ts index cdbb3d472385..7e69fc80d4a0 100644 --- a/apps/meteor/server/startup/migrations/v316.ts +++ b/apps/meteor/server/startup/migrations/v316.ts @@ -22,6 +22,10 @@ addMigration({ await Settings.removeById('Cloud_Workspace_Access_Token_Expires_At'); } - await WorkspaceCredentials.updateCredentialByScope('', accessToken, accessTokenExpiresAt); + await WorkspaceCredentials.updateCredentialByScope({ + scope: '', + accessToken, + expirationDate: accessTokenExpiresAt, + }); }, }); From f464772a084cf284df5810f77cfaa1dc593202ea Mon Sep 17 00:00:00 2001 From: gustrb Date: Wed, 16 Oct 2024 16:15:47 -0300 Subject: [PATCH 19/19] fix: apply suggestions --- .changeset/plenty-hairs-camp.md | 3 ++- apps/meteor/app/api/server/v1/misc.ts | 2 +- .../ee/server/models/WorkspaceCredentials.ts | 3 +-- .../ee/server/models/raw/WorkspaceCredentials.ts | 14 +++++++------- apps/meteor/server/startup/migrations/v316.ts | 8 ++++---- .../src/models/IWorkspaceCredentialsModel.ts | 4 ++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.changeset/plenty-hairs-camp.md b/.changeset/plenty-hairs-camp.md index 3e26790fe9fd..e75743f4a863 100644 --- a/.changeset/plenty-hairs-camp.md +++ b/.changeset/plenty-hairs-camp.md @@ -5,4 +5,5 @@ "@rocket.chat/models": major --- -Now, we are not fetching cloud credentials from a cache, by doing so, we are avoiding the use of stale cloud credentials when comunicating with cloud services. We are, also, using a new collection to store the credentials and their scopes. +Adds a new collection to store all the workspace cloud tokens to defer the race condition management to MongoDB instead of having to handle it within the settings cache. +Removes the Cloud_Workspace_Access_Token & Cloud_Workspace_Access_Token_Expires_At settings since they are not going to be used anymore. \ No newline at end of file diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index cf8ece115d36..5cd522d20533 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -664,7 +664,7 @@ API.v1.addRoute( const settingsIds: string[] = []; if (this.bodyParams.setDeploymentAs === 'new-workspace') { - await WorkspaceCredentials.unsetCredentialByScope(''); + await WorkspaceCredentials.unsetCredentialByScope(); settingsIds.push( 'Cloud_Service_Agree_PrivacyTerms', 'Cloud_Workspace_Id', diff --git a/apps/meteor/ee/server/models/WorkspaceCredentials.ts b/apps/meteor/ee/server/models/WorkspaceCredentials.ts index d4f5cd2d912d..26b1f015f067 100644 --- a/apps/meteor/ee/server/models/WorkspaceCredentials.ts +++ b/apps/meteor/ee/server/models/WorkspaceCredentials.ts @@ -1,7 +1,6 @@ import { registerModel } from '@rocket.chat/models'; -import { trashCollection } from '../../../server/database/trash'; import { db } from '../../../server/database/utils'; import { WorkspaceCredentialsRaw } from './raw/WorkspaceCredentials'; -registerModel('IWorkspaceCredentialsModel', new WorkspaceCredentialsRaw(db, trashCollection)); +registerModel('IWorkspaceCredentialsModel', new WorkspaceCredentialsRaw(db)); diff --git a/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts b/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts index 814bc365a338..f4141967814d 100644 --- a/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts +++ b/apps/meteor/ee/server/models/raw/WorkspaceCredentials.ts @@ -1,19 +1,19 @@ -import type { RocketChatRecordDeleted, IWorkspaceCredentials } from '@rocket.chat/core-typings'; +import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; import type { IWorkspaceCredentialsModel } from '@rocket.chat/model-typings'; -import type { Collection, Db, DeleteResult, Filter, IndexDescription, UpdateResult } from 'mongodb'; +import type { Db, DeleteResult, Filter, IndexDescription, UpdateResult } from 'mongodb'; import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; export class WorkspaceCredentialsRaw extends BaseRaw implements IWorkspaceCredentialsModel { - constructor(db: Db, trash?: Collection>) { - super(db, 'workspace_credentials', trash); + constructor(db: Db) { + super(db, 'workspace_credentials'); } protected modelIndexes(): IndexDescription[] { - return [{ key: { _id: 1, scopes: 1, expirationDate: 1, accessToken: 1 }, unique: true }]; + return [{ key: { scopes: 1, expirationDate: 1, accessToken: 1 }, unique: true }]; } - getCredentialByScope(scope: string): Promise { + getCredentialByScope(scope = ''): Promise { const query: Filter = { scopes: { $all: [scope], @@ -24,7 +24,7 @@ export class WorkspaceCredentialsRaw extends BaseRaw impl return this.findOne(query); } - unsetCredentialByScope(scope: string): Promise { + unsetCredentialByScope(scope = ''): Promise { const query: Filter = { scopes: { $all: [scope], diff --git a/apps/meteor/server/startup/migrations/v316.ts b/apps/meteor/server/startup/migrations/v316.ts index 7e69fc80d4a0..c8641b896e77 100644 --- a/apps/meteor/server/startup/migrations/v316.ts +++ b/apps/meteor/server/startup/migrations/v316.ts @@ -6,26 +6,26 @@ addMigration({ version: 316, name: 'Remove Cloud_Workspace_Access_Token and Cloud_Workspace_Access_Token_Expires_At from the settings collection and add to the WorkspaceCredentials collection', async up() { - const workspaceCredentials = await WorkspaceCredentials.getCredentialByScope(''); + const workspaceCredentials = await WorkspaceCredentials.getCredentialByScope(); if (workspaceCredentials) { return; } const accessToken = ((await Settings.getValueById('Cloud_Workspace_Access_Token')) as string) || ''; - const accessTokenExpiresAt = ((await Settings.getValueById('Cloud_Workspace_Access_Token_Expires_At')) as Date) || new Date(0); + const expirationDate = ((await Settings.getValueById('Cloud_Workspace_Access_Token_Expires_At')) as Date) || new Date(0); if (accessToken) { await Settings.removeById('Cloud_Workspace_Access_Token'); } - if (accessTokenExpiresAt) { + if (expirationDate) { await Settings.removeById('Cloud_Workspace_Access_Token_Expires_At'); } await WorkspaceCredentials.updateCredentialByScope({ scope: '', accessToken, - expirationDate: accessTokenExpiresAt, + expirationDate, }); }, }); diff --git a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts index 303da0ff1cf9..58b9a8a5049d 100644 --- a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts +++ b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts @@ -4,8 +4,8 @@ import type { DeleteResult, UpdateResult } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; export interface IWorkspaceCredentialsModel extends IBaseModel { - getCredentialByScope(scope: string): Promise; - unsetCredentialByScope(scope: string): Promise; + getCredentialByScope(scope?: string): Promise; + unsetCredentialByScope(scope?: string): Promise; updateCredentialByScope(credentials: { scope: string; accessToken: string; expirationDate: Date }): Promise; removeAllCredentials(): Promise; }