diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index 5cd522d20533..c026236231d8 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -664,7 +664,8 @@ API.v1.addRoute( const settingsIds: string[] = []; if (this.bodyParams.setDeploymentAs === 'new-workspace') { - await WorkspaceCredentials.unsetCredentialByScope(); + await WorkspaceCredentials.removeAllCredentials(); + settingsIds.push( 'Cloud_Service_Agree_PrivacyTerms', 'Cloud_Workspace_Id', diff --git a/apps/meteor/app/apps/server/bridges/cloud.ts b/apps/meteor/app/apps/server/bridges/cloud.ts index 30ca897240f8..0f908ccfe0a3 100644 --- a/apps/meteor/app/apps/server/bridges/cloud.ts +++ b/apps/meteor/app/apps/server/bridges/cloud.ts @@ -12,7 +12,7 @@ export class AppCloudBridge extends CloudWorkspaceBridge { public async getWorkspaceToken(scope: string, appId: string): Promise { this.orch.debugLog(`App ${appId} is getting the workspace's token`); - const token = await getWorkspaceAccessTokenWithScope(scope); + const token = await getWorkspaceAccessTokenWithScope({ scope }); return token; } diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts index 93cfa3266ecf..6595c8e90fc4 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessToken.ts @@ -1,6 +1,8 @@ import type { IWorkspaceCredentials } from '@rocket.chat/core-typings'; import { WorkspaceCredentials } from '@rocket.chat/models'; +import { SystemLogger } from '../../../../server/lib/logger/system'; +import { workspaceScopes } from '../oauthScopes'; import { getWorkspaceAccessTokenWithScope } from './getWorkspaceAccessTokenWithScope'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; @@ -24,20 +26,27 @@ export async function getWorkspaceAccessToken(forceNew = false, scope = '', save return ''; } - const workspaceCredentials = await WorkspaceCredentials.getCredentialByScope(scope); - if (!workspaceCredentials) { - throw new CloudWorkspaceAccessTokenError(); + // Note: If no scope is given, it means we should assume the default scope, we store the default scopes + // in the global variable workspaceScopes. + if (scope === '') { + scope = workspaceScopes.join(' '); } - if (!hasWorkspaceAccessTokenExpired(workspaceCredentials) && !forceNew) { + const workspaceCredentials = await WorkspaceCredentials.getCredentialByScope(scope); + if (workspaceCredentials && !hasWorkspaceAccessTokenExpired(workspaceCredentials) && !forceNew) { + SystemLogger.debug( + `Workspace credentials cache hit using scope: ${scope}. Avoiding generating a new access token from cloud services.`, + ); return workspaceCredentials.accessToken; } - const accessToken = await getWorkspaceAccessTokenWithScope(scope, throwOnError); + SystemLogger.debug(`Workspace credentials cache miss using scope: ${scope}, fetching new access token from cloud services.`); + + const accessToken = await getWorkspaceAccessTokenWithScope({ scope, throwOnError }); if (save) { await WorkspaceCredentials.updateCredentialByScope({ - scope, + scope: accessToken.scope, accessToken: accessToken.token, expirationDate: accessToken.expiresAt, }); diff --git a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts index 3a04031ebb88..1137b899967a 100644 --- a/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts +++ b/apps/meteor/app/cloud/server/functions/getWorkspaceAccessTokenWithScope.ts @@ -8,10 +8,24 @@ import { CloudWorkspaceAccessTokenError } from './getWorkspaceAccessToken'; import { removeWorkspaceRegistrationInfo } from './removeWorkspaceRegistrationInfo'; import { retrieveRegistrationStatus } from './retrieveRegistrationStatus'; -export async function getWorkspaceAccessTokenWithScope(scope = '', throwOnError = false) { +type WorkspaceAccessTokenWithScope = { + token: string; + expiresAt: Date; + scope: string; +}; + +type GetWorkspaceAccessTokenWithScopeParams = { + scope?: string; + throwOnError?: boolean; +}; + +export async function getWorkspaceAccessTokenWithScope({ + scope = '', + throwOnError = false, +}: GetWorkspaceAccessTokenWithScopeParams): Promise { const { workspaceRegistered } = await retrieveRegistrationStatus(); - const tokenResponse = { token: '', expiresAt: new Date() }; + const tokenResponse = { token: '', expiresAt: new Date(), scope: '' }; if (!workspaceRegistered) { return tokenResponse; @@ -62,6 +76,7 @@ export async function getWorkspaceAccessTokenWithScope(scope = '', throwOnError return { token: payload.access_token, expiresAt, + scope: payload.scope, }; } catch (err: any) { if (err instanceof CloudWorkspaceAccessTokenError) { diff --git a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts index 746904687d67..63633c567845 100644 --- a/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/saveRegistrationData.ts @@ -1,5 +1,5 @@ import { applyLicense } from '@rocket.chat/license'; -import { Settings, WorkspaceCredentials } from '@rocket.chat/models'; +import { Settings } from '@rocket.chat/models'; import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; import { settings } from '../../../settings/server'; @@ -59,12 +59,6 @@ async function saveRegistrationDataBase({ { _id: 'Cloud_Workspace_Registration_Client_Uri', value: registration_client_uri }, ]; - 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) => { diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index fc597d00857c..0283f2eef783 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -189,7 +189,7 @@ export class AppsRestApi { return API.v1.failure({ error: 'Invalid purchase type' }); } - const response = await getWorkspaceAccessTokenWithScope('marketplace:purchase'); + const response = await getWorkspaceAccessTokenWithScope({ scope: 'marketplace:purchase' }); if (!response.token) { return API.v1.failure({ error: 'Unauthorized' }); } @@ -289,7 +289,7 @@ export class AppsRestApi { return API.v1.failure({ error: 'Invalid purchase type' }); } - const token = await getWorkspaceAccessTokenWithScope('marketplace:purchase'); + const token = await getWorkspaceAccessTokenWithScope({ scope: 'marketplace:purchase' }); if (!token) { return API.v1.failure({ error: 'Unauthorized' }); } diff --git a/apps/meteor/server/models/raw/WorkspaceCredentials.ts b/apps/meteor/server/models/raw/WorkspaceCredentials.ts index b989ace8c2a3..09706cdec4bb 100644 --- a/apps/meteor/server/models/raw/WorkspaceCredentials.ts +++ b/apps/meteor/server/models/raw/WorkspaceCredentials.ts @@ -10,31 +10,15 @@ export class WorkspaceCredentialsRaw extends BaseRaw impl } protected modelIndexes(): IndexDescription[] { - return [{ key: { scopes: 1, expirationDate: 1, accessToken: 1 }, unique: true }]; + return [{ key: { scope: 1, expirationDate: 1, accessToken: 1 }, unique: true }]; } getCredentialByScope(scope = ''): Promise { - const query: Filter = { - scopes: { - $all: [scope], - $size: 1, - }, - }; + const query: Filter = { scope }; return this.findOne(query); } - unsetCredentialByScope(scope = ''): Promise { - const query: Filter = { - scopes: { - $all: [scope], - $size: 1, - }, - }; - - return this.deleteOne(query); - } - updateCredentialByScope({ scope, accessToken, @@ -46,18 +30,13 @@ export class WorkspaceCredentialsRaw extends BaseRaw impl }): Promise { const record = { $set: { - scopes: [scope], + scope, accessToken, expirationDate, }, }; - const query: Filter = { - scopes: { - $all: [scope], - $size: 1, - }, - }; + const query: Filter = { scope }; return this.updateOne(query, record, { upsert: true }); } diff --git a/apps/meteor/server/startup/migrations/v316.ts b/apps/meteor/server/startup/migrations/v316.ts index c8641b896e77..210dafc4483f 100644 --- a/apps/meteor/server/startup/migrations/v316.ts +++ b/apps/meteor/server/startup/migrations/v316.ts @@ -1,16 +1,11 @@ -import { Settings, WorkspaceCredentials } from '@rocket.chat/models'; +import { Settings } 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', + name: 'Remove Cloud_Workspace_Access_Token and Cloud_Workspace_Access_Token_Expires_At from the settings collection', async up() { - const workspaceCredentials = await WorkspaceCredentials.getCredentialByScope(); - if (workspaceCredentials) { - return; - } - const accessToken = ((await Settings.getValueById('Cloud_Workspace_Access_Token')) as string) || ''; const expirationDate = ((await Settings.getValueById('Cloud_Workspace_Access_Token_Expires_At')) as Date) || new Date(0); @@ -21,11 +16,5 @@ addMigration({ if (expirationDate) { await Settings.removeById('Cloud_Workspace_Access_Token_Expires_At'); } - - await WorkspaceCredentials.updateCredentialByScope({ - scope: '', - accessToken, - expirationDate, - }); }, }); diff --git a/packages/core-typings/src/ee/IWorkspaceCredentials.ts b/packages/core-typings/src/ee/IWorkspaceCredentials.ts index 1acf4570f3cf..1fda00d0c8b3 100644 --- a/packages/core-typings/src/ee/IWorkspaceCredentials.ts +++ b/packages/core-typings/src/ee/IWorkspaceCredentials.ts @@ -2,7 +2,7 @@ import type { IRocketChatRecord } from '../IRocketChatRecord'; export interface IWorkspaceCredentials extends IRocketChatRecord { _id: string; - scopes: string[]; + scope: string; expirationDate: Date; accessToken: string; } diff --git a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts index 58b9a8a5049d..fa13dfa82977 100644 --- a/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts +++ b/packages/model-typings/src/models/IWorkspaceCredentialsModel.ts @@ -5,7 +5,6 @@ import type { IBaseModel } from './IBaseModel'; export interface IWorkspaceCredentialsModel extends IBaseModel { getCredentialByScope(scope?: string): Promise; - unsetCredentialByScope(scope?: string): Promise; updateCredentialByScope(credentials: { scope: string; accessToken: string; expirationDate: Date }): Promise; removeAllCredentials(): Promise; }