From c754fad268ce5cd4af794ed8a0702123b616a94b Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Thu, 30 Apr 2020 17:20:56 -0300 Subject: [PATCH 01/12] Fix security problem --- src/server/rooms/Room.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/server/rooms/Room.ts b/src/server/rooms/Room.ts index 0c787c004..bf32819c8 100644 --- a/src/server/rooms/Room.ts +++ b/src/server/rooms/Room.ts @@ -2,6 +2,8 @@ import { IRoom, RoomType } from '../../definition/rooms'; import { IUser } from '../../definition/users'; import { AppManager } from '../AppManager'; +const PrivateManager = Symbol('RoomPrivateManager'); + export class Room implements IRoom { public id: string; public displayName?: string; @@ -19,13 +21,15 @@ export class Room implements IRoom { public userIds?: Array; private _USERNAMES: Array; + private [PrivateManager]: AppManager; + /** * @deprecated */ public get usernames(): Array { // Get usernames if (!this._USERNAMES) { - this._USERNAMES = this.manager.getBridges().getInternalBridge().getUsernamesOfRoomById(this.id); + this._USERNAMES = this[PrivateManager].getBridges().getInternalBridge().getUsernamesOfRoomById(this.id); } return this._USERNAMES; @@ -35,8 +39,15 @@ export class Room implements IRoom { return; } - public constructor(room: IRoom, private manager: AppManager) { + public constructor(room: IRoom, manager: AppManager) { Object.assign(this, room); + + Object.defineProperty(this, PrivateManager, { + configurable: false, + enumerable: false, + writable: false, + value: manager, + }); } get value(): object { From 1edda3c1d6f6a202f94e329bf27b7ca4dc84d7ba Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 1 May 2020 18:49:20 -0300 Subject: [PATCH 02/12] Make calls to apps' method rethrow AppsEngineError instances --- src/definition/errors/AppsEngineError.ts | 1 + src/definition/errors/index.ts | 1 + src/server/ProxiedApp.ts | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/definition/errors/AppsEngineError.ts create mode 100644 src/definition/errors/index.ts diff --git a/src/definition/errors/AppsEngineError.ts b/src/definition/errors/AppsEngineError.ts new file mode 100644 index 000000000..f03c6e97c --- /dev/null +++ b/src/definition/errors/AppsEngineError.ts @@ -0,0 +1 @@ +export class AppsEngineError extends Error {} diff --git a/src/definition/errors/index.ts b/src/definition/errors/index.ts new file mode 100644 index 000000000..1114a8486 --- /dev/null +++ b/src/definition/errors/index.ts @@ -0,0 +1 @@ +export { AppsEngineError } from './AppsEngineError'; diff --git a/src/server/ProxiedApp.ts b/src/server/ProxiedApp.ts index dbb929744..092dd175b 100644 --- a/src/server/ProxiedApp.ts +++ b/src/server/ProxiedApp.ts @@ -1,8 +1,8 @@ import * as vm from 'vm'; - import { IAppAccessors, ILogger } from '../definition/accessors'; import { App } from '../definition/App'; import { AppStatus } from '../definition/AppStatus'; +import { AppsEngineError } from '../definition/errors'; import { IApp } from '../definition/IApp'; import { AppMethod, IAppAuthorInfo, IAppInfo } from '../definition/metadata'; import { AppManager } from './AppManager'; @@ -88,6 +88,10 @@ export class ProxiedApp implements IApp { } catch (e) { logger.error(e); logger.debug(`'${method}' was unsuccessful.`); + + if (e instanceof AppsEngineError) { + throw e; + } } this.manager.getLogStorage().storeEntries(this.getID(), logger); From 3bf572c111e54bdf6a1309bcf0415b8dee008986 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 1 May 2020 18:49:43 -0300 Subject: [PATCH 03/12] Add IPreRoomUserJoined event --- src/definition/metadata/AppMethod.ts | 1 + src/definition/rooms/IPreRoomUserJoined.ts | 16 +++++++++++++++ src/definition/rooms/index.ts | 2 ++ src/server/compiler/AppImplements.ts | 1 + src/server/managers/AppListenerManager.ts | 23 ++++++++++++++++++++-- 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/definition/rooms/IPreRoomUserJoined.ts diff --git a/src/definition/metadata/AppMethod.ts b/src/definition/metadata/AppMethod.ts index 56ecacfd1..473c64376 100644 --- a/src/definition/metadata/AppMethod.ts +++ b/src/definition/metadata/AppMethod.ts @@ -43,6 +43,7 @@ export enum AppMethod { EXECUTEPREROOMDELETEPREVENT = 'executePreRoomDeletePrevent', CHECKPOSTROOMDELETED = 'checkPostRoomDeleted', EXECUTEPOSTROOMDELETED = 'executePostRoomDeleted', + EXECUTE_PRE_ROOM_USER_JOINED = 'executePreRoomUserJoined', // External Component handlers EXECUTEPOSTEXTERNALCOMPONENTOPENED = 'executePostExternalComponentOpened', EXECUTEPOSTEXTERNALCOMPONENTCLOSED = 'executePostExternalComponentClosed', diff --git a/src/definition/rooms/IPreRoomUserJoined.ts b/src/definition/rooms/IPreRoomUserJoined.ts new file mode 100644 index 000000000..6e1f8964e --- /dev/null +++ b/src/definition/rooms/IPreRoomUserJoined.ts @@ -0,0 +1,16 @@ +import { IHttp, IPersistence, IRead } from '../accessors'; +import { AppsEngineError } from '../errors'; +import { IUser } from '../users'; +import { IRoom } from './IRoom'; + +export interface IPreRoomUserJoined { + executePreRoomUserJoined(context: IPreRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; +} + +export interface IPreRoomUserJoinedContext { + joiningUser: IUser; + room: IRoom; + invitingUser?: IUser; +} + +export class UserCannotJoinError extends AppsEngineError {} diff --git a/src/definition/rooms/index.ts b/src/definition/rooms/index.ts index 3e83b3181..fcfe18fa3 100644 --- a/src/definition/rooms/index.ts +++ b/src/definition/rooms/index.ts @@ -17,3 +17,5 @@ export { IPreRoomCreatePrevent, IPreRoomDeletePrevent, }; + +export * from './IPreRoomUserJoined'; diff --git a/src/server/compiler/AppImplements.ts b/src/server/compiler/AppImplements.ts index 78890505e..37dd6df59 100644 --- a/src/server/compiler/AppImplements.ts +++ b/src/server/compiler/AppImplements.ts @@ -19,6 +19,7 @@ export enum AppInterface { IPostRoomCreate = 'IPostRoomCreate', IPreRoomDeletePrevent = 'IPreRoomDeletePrevent', IPostRoomDeleted = 'IPostRoomDeleted', + IPreRoomUserJoined = 'IPreRoomUserJoined', // External Components IPostExternalComponentOpened = 'IPostExternalComponentOpened', IPostExternalComponentClosed = 'IPostExternalComponentClosed', diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index d046184a8..5e6bb7bd4 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -2,7 +2,7 @@ import { IExternalComponent } from '../../definition/externalComponent'; import { ILivechatEventContext, ILivechatRoom } from '../../definition/livechat'; import { IMessage } from '../../definition/messages'; import { AppMethod } from '../../definition/metadata'; -import { IRoom } from '../../definition/rooms'; +import { IPreRoomUserJoinedContext, IRoom } from '../../definition/rooms'; import { IUIKitIncomingInteraction, IUIKitResponse, IUIKitView, UIKitIncomingInteractionType } from '../../definition/uikit'; import { IUIKitIncomingInteractionMessageContainer, @@ -64,7 +64,7 @@ export class AppListenerManager { } // tslint:disable-next-line - public async executeListener(int: AppInterface, data: IMessage | IRoom | IUser | ILivechatRoom | IUIKitIncomingInteraction | IExternalComponent | ILivechatEventContext): Promise { + public async executeListener(int: AppInterface, data: IMessage | IRoom | IUser | ILivechatRoom | IUIKitIncomingInteraction | IExternalComponent | ILivechatEventContext | IPreRoomUserJoinedContext): Promise { switch (int) { // Messages case AppInterface.IPreMessageSentPrevent: @@ -105,6 +105,8 @@ export class AppListenerManager { case AppInterface.IPostRoomDeleted: this.executePostRoomDeleted(data as IRoom); return; + case AppInterface.IPreRoomUserJoined: + return this.executePreRoomUserJoined(data as IPreRoomUserJoinedContext); // External Components case AppInterface.IPostExternalComponentOpened: this.executePostExternalComponentOpened(data as IExternalComponent); @@ -618,6 +620,23 @@ export class AppListenerManager { } } + private async executePreRoomUserJoined(data: IPreRoomUserJoinedContext): Promise { + data.room = new Room(data.room, this.manager); + + for (const appId of this.listeners.get(AppInterface.IPreRoomUserJoined)) { + const app = this.manager.getOneById(appId); + + if (app.hasMethod(AppMethod.EXECUTE_PRE_ROOM_USER_JOINED)) { + await app.call(AppMethod.EXECUTE_PRE_ROOM_USER_JOINED, + data, + this.am.getReader(appId), + this.am.getHttp(appId), + this.am.getPersistence(appId), + ); + } + } + } + // External Components private async executePostExternalComponentOpened(data: IExternalComponent): Promise { const cfExternalComponent = Utilities.deepCloneAndFreeze(data); From 0ab131cf2ddeff1d59fefc6698deb7c628597e15 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Sun, 3 May 2020 22:26:56 -0300 Subject: [PATCH 04/12] Deep clone and freeze external data in the new event --- src/server/managers/AppListenerManager.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index 5e6bb7bd4..43bf33115 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -620,9 +620,13 @@ export class AppListenerManager { } } - private async executePreRoomUserJoined(data: IPreRoomUserJoinedContext): Promise { + private async executePreRoomUserJoined(externalData: IPreRoomUserJoinedContext): Promise { + const data = Utilities.deepClone(externalData); + data.room = new Room(data.room, this.manager); + Utilities.deepFreeze(data); + for (const appId of this.listeners.get(AppInterface.IPreRoomUserJoined)) { const app = this.manager.getOneById(appId); From a400a81d66b635e990908b4139dab92afd35cca9 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 5 May 2020 14:39:24 -0300 Subject: [PATCH 05/12] Add the IPostRoomUserJoined event --- src/definition/metadata/AppMethod.ts | 1 + src/definition/rooms/IPostRoomUserJoined.ts | 6 ++++ src/definition/rooms/IPreRoomUserJoined.ts | 11 ++----- .../rooms/IRoomUserJoinedContext.ts | 8 +++++ src/definition/rooms/index.ts | 2 ++ src/server/compiler/AppImplements.ts | 1 + src/server/managers/AppListenerManager.ts | 31 ++++++++++++++++--- 7 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 src/definition/rooms/IPostRoomUserJoined.ts create mode 100644 src/definition/rooms/IRoomUserJoinedContext.ts diff --git a/src/definition/metadata/AppMethod.ts b/src/definition/metadata/AppMethod.ts index 473c64376..968d084e0 100644 --- a/src/definition/metadata/AppMethod.ts +++ b/src/definition/metadata/AppMethod.ts @@ -44,6 +44,7 @@ export enum AppMethod { CHECKPOSTROOMDELETED = 'checkPostRoomDeleted', EXECUTEPOSTROOMDELETED = 'executePostRoomDeleted', EXECUTE_PRE_ROOM_USER_JOINED = 'executePreRoomUserJoined', + EXECUTE_POST_ROOM_USER_JOINED = 'executePostRoomUserJoined', // External Component handlers EXECUTEPOSTEXTERNALCOMPONENTOPENED = 'executePostExternalComponentOpened', EXECUTEPOSTEXTERNALCOMPONENTCLOSED = 'executePostExternalComponentClosed', diff --git a/src/definition/rooms/IPostRoomUserJoined.ts b/src/definition/rooms/IPostRoomUserJoined.ts new file mode 100644 index 000000000..cacd5c64c --- /dev/null +++ b/src/definition/rooms/IPostRoomUserJoined.ts @@ -0,0 +1,6 @@ +import { IHttp, IPersistence, IRead } from '../accessors'; +import { IRoomUserJoinedContext } from './IRoomUserJoinedContext'; + +export interface IPostRoomUserJoined { + executePostRoomUserJoined(context: IRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; +} diff --git a/src/definition/rooms/IPreRoomUserJoined.ts b/src/definition/rooms/IPreRoomUserJoined.ts index 6e1f8964e..ebc6e27dd 100644 --- a/src/definition/rooms/IPreRoomUserJoined.ts +++ b/src/definition/rooms/IPreRoomUserJoined.ts @@ -1,16 +1,9 @@ import { IHttp, IPersistence, IRead } from '../accessors'; import { AppsEngineError } from '../errors'; -import { IUser } from '../users'; -import { IRoom } from './IRoom'; +import { IRoomUserJoinedContext } from './IRoomUserJoinedContext'; export interface IPreRoomUserJoined { - executePreRoomUserJoined(context: IPreRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; -} - -export interface IPreRoomUserJoinedContext { - joiningUser: IUser; - room: IRoom; - invitingUser?: IUser; + executePreRoomUserJoined(context: IRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; } export class UserCannotJoinError extends AppsEngineError {} diff --git a/src/definition/rooms/IRoomUserJoinedContext.ts b/src/definition/rooms/IRoomUserJoinedContext.ts new file mode 100644 index 000000000..19c76572b --- /dev/null +++ b/src/definition/rooms/IRoomUserJoinedContext.ts @@ -0,0 +1,8 @@ +import { IUser } from '../users'; +import { IRoom } from './IRoom'; + +export interface IRoomUserJoinedContext { + joiningUser: IUser; + room: IRoom; + invitingUser?: IUser; +} diff --git a/src/definition/rooms/index.ts b/src/definition/rooms/index.ts index fcfe18fa3..12b88c923 100644 --- a/src/definition/rooms/index.ts +++ b/src/definition/rooms/index.ts @@ -19,3 +19,5 @@ export { }; export * from './IPreRoomUserJoined'; +export * from './IPostRoomUserJoined'; +export * from './IRoomUserJoinedContext'; diff --git a/src/server/compiler/AppImplements.ts b/src/server/compiler/AppImplements.ts index 37dd6df59..1894ab38f 100644 --- a/src/server/compiler/AppImplements.ts +++ b/src/server/compiler/AppImplements.ts @@ -20,6 +20,7 @@ export enum AppInterface { IPreRoomDeletePrevent = 'IPreRoomDeletePrevent', IPostRoomDeleted = 'IPostRoomDeleted', IPreRoomUserJoined = 'IPreRoomUserJoined', + IPostRoomUserJoined = 'IPostRoomUserJoined', // External Components IPostExternalComponentOpened = 'IPostExternalComponentOpened', IPostExternalComponentClosed = 'IPostExternalComponentClosed', diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index 43bf33115..7042ec572 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -2,7 +2,7 @@ import { IExternalComponent } from '../../definition/externalComponent'; import { ILivechatEventContext, ILivechatRoom } from '../../definition/livechat'; import { IMessage } from '../../definition/messages'; import { AppMethod } from '../../definition/metadata'; -import { IPreRoomUserJoinedContext, IRoom } from '../../definition/rooms'; +import { IRoom, IRoomUserJoinedContext } from '../../definition/rooms'; import { IUIKitIncomingInteraction, IUIKitResponse, IUIKitView, UIKitIncomingInteractionType } from '../../definition/uikit'; import { IUIKitIncomingInteractionMessageContainer, @@ -64,7 +64,7 @@ export class AppListenerManager { } // tslint:disable-next-line - public async executeListener(int: AppInterface, data: IMessage | IRoom | IUser | ILivechatRoom | IUIKitIncomingInteraction | IExternalComponent | ILivechatEventContext | IPreRoomUserJoinedContext): Promise { + public async executeListener(int: AppInterface, data: IMessage | IRoom | IUser | ILivechatRoom | IUIKitIncomingInteraction | IExternalComponent | ILivechatEventContext | IRoomUserJoinedContext): Promise { switch (int) { // Messages case AppInterface.IPreMessageSentPrevent: @@ -106,7 +106,9 @@ export class AppListenerManager { this.executePostRoomDeleted(data as IRoom); return; case AppInterface.IPreRoomUserJoined: - return this.executePreRoomUserJoined(data as IPreRoomUserJoinedContext); + return this.executePreRoomUserJoined(data as IRoomUserJoinedContext); + case AppInterface.IPostRoomUserJoined: + return this.executePostRoomUserJoined(data as IRoomUserJoinedContext); // External Components case AppInterface.IPostExternalComponentOpened: this.executePostExternalComponentOpened(data as IExternalComponent); @@ -620,7 +622,7 @@ export class AppListenerManager { } } - private async executePreRoomUserJoined(externalData: IPreRoomUserJoinedContext): Promise { + private async executePreRoomUserJoined(externalData: IRoomUserJoinedContext): Promise { const data = Utilities.deepClone(externalData); data.room = new Room(data.room, this.manager); @@ -641,6 +643,27 @@ export class AppListenerManager { } } + private async executePostRoomUserJoined(externalData: IRoomUserJoinedContext): Promise { + const data = Utilities.deepClone(externalData); + + data.room = new Room(data.room, this.manager); + + Utilities.deepFreeze(data); + + for (const appId of this.listeners.get(AppInterface.IPostRoomUserJoined)) { + const app = this.manager.getOneById(appId); + + if (app.hasMethod(AppMethod.EXECUTE_POST_ROOM_USER_JOINED)) { + await app.call(AppMethod.EXECUTE_POST_ROOM_USER_JOINED, + data, + this.am.getReader(appId), + this.am.getHttp(appId), + this.am.getPersistence(appId), + ); + } + } + } + // External Components private async executePostExternalComponentOpened(data: IExternalComponent): Promise { const cfExternalComponent = Utilities.deepCloneAndFreeze(data); From 8a9b347ec8014997272b8c92e9681313b3db5832 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 5 May 2020 15:29:20 -0300 Subject: [PATCH 06/12] Change freezing strategy --- src/server/managers/AppListenerManager.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index 7042ec572..2d886a95f 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -625,9 +625,12 @@ export class AppListenerManager { private async executePreRoomUserJoined(externalData: IRoomUserJoinedContext): Promise { const data = Utilities.deepClone(externalData); - data.room = new Room(data.room, this.manager); + data.room = new Room(Utilities.deepFreeze(data.room), this.manager); + Utilities.deepFreeze(data.joiningUser); - Utilities.deepFreeze(data); + if (data.invitingUser) { + Utilities.deepFreeze(data.invitingUser); + } for (const appId of this.listeners.get(AppInterface.IPreRoomUserJoined)) { const app = this.manager.getOneById(appId); @@ -646,9 +649,12 @@ export class AppListenerManager { private async executePostRoomUserJoined(externalData: IRoomUserJoinedContext): Promise { const data = Utilities.deepClone(externalData); - data.room = new Room(data.room, this.manager); + data.room = new Room(Utilities.deepFreeze(data.room), this.manager); + Utilities.deepFreeze(data.joiningUser); - Utilities.deepFreeze(data); + if (data.invitingUser) { + Utilities.deepFreeze(data.invitingUser); + } for (const appId of this.listeners.get(AppInterface.IPostRoomUserJoined)) { const app = this.manager.getOneById(appId); From bcdd3b0f252ccba7d675be65ea1f720717b2b265 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 8 May 2020 18:09:36 -0300 Subject: [PATCH 07/12] Rename new Errors as Exceptions --- src/definition/errors/AppsEngineError.ts | 1 - src/definition/errors/index.ts | 1 - src/definition/exceptions/AppsEngineException.ts | 1 + src/definition/exceptions/index.ts | 1 + src/definition/rooms/IPreRoomUserJoined.ts | 4 ++-- src/server/ProxiedApp.ts | 4 ++-- 6 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 src/definition/errors/AppsEngineError.ts delete mode 100644 src/definition/errors/index.ts create mode 100644 src/definition/exceptions/AppsEngineException.ts create mode 100644 src/definition/exceptions/index.ts diff --git a/src/definition/errors/AppsEngineError.ts b/src/definition/errors/AppsEngineError.ts deleted file mode 100644 index f03c6e97c..000000000 --- a/src/definition/errors/AppsEngineError.ts +++ /dev/null @@ -1 +0,0 @@ -export class AppsEngineError extends Error {} diff --git a/src/definition/errors/index.ts b/src/definition/errors/index.ts deleted file mode 100644 index 1114a8486..000000000 --- a/src/definition/errors/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AppsEngineError } from './AppsEngineError'; diff --git a/src/definition/exceptions/AppsEngineException.ts b/src/definition/exceptions/AppsEngineException.ts new file mode 100644 index 000000000..7d34164cd --- /dev/null +++ b/src/definition/exceptions/AppsEngineException.ts @@ -0,0 +1 @@ +export class AppsEngineException extends Error {} diff --git a/src/definition/exceptions/index.ts b/src/definition/exceptions/index.ts new file mode 100644 index 000000000..92b2db2ad --- /dev/null +++ b/src/definition/exceptions/index.ts @@ -0,0 +1 @@ +export { AppsEngineException } from './AppsEngineException'; diff --git a/src/definition/rooms/IPreRoomUserJoined.ts b/src/definition/rooms/IPreRoomUserJoined.ts index ebc6e27dd..d28165a76 100644 --- a/src/definition/rooms/IPreRoomUserJoined.ts +++ b/src/definition/rooms/IPreRoomUserJoined.ts @@ -1,9 +1,9 @@ import { IHttp, IPersistence, IRead } from '../accessors'; -import { AppsEngineError } from '../errors'; +import { AppsEngineException } from '../exceptions'; import { IRoomUserJoinedContext } from './IRoomUserJoinedContext'; export interface IPreRoomUserJoined { executePreRoomUserJoined(context: IRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; } -export class UserCannotJoinError extends AppsEngineError {} +export class UserNotAllowedJoinException extends AppsEngineException {} diff --git a/src/server/ProxiedApp.ts b/src/server/ProxiedApp.ts index 092dd175b..85e03095c 100644 --- a/src/server/ProxiedApp.ts +++ b/src/server/ProxiedApp.ts @@ -2,7 +2,7 @@ import * as vm from 'vm'; import { IAppAccessors, ILogger } from '../definition/accessors'; import { App } from '../definition/App'; import { AppStatus } from '../definition/AppStatus'; -import { AppsEngineError } from '../definition/errors'; +import { AppsEngineException } from '../definition/exceptions'; import { IApp } from '../definition/IApp'; import { AppMethod, IAppAuthorInfo, IAppInfo } from '../definition/metadata'; import { AppManager } from './AppManager'; @@ -89,7 +89,7 @@ export class ProxiedApp implements IApp { logger.error(e); logger.debug(`'${method}' was unsuccessful.`); - if (e instanceof AppsEngineError) { + if (e instanceof AppsEngineException) { throw e; } } From e6549659d2519238a3358bffd83b19a1bf7babac Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Sun, 17 May 2020 19:52:43 -0300 Subject: [PATCH 08/12] New "essentials" mechanism (#269) * Move AppInterface definition * Lock events on apps load --- src/definition/AppStatus.ts | 7 ++ src/definition/metadata/AppInterface.ts | 36 +++++++++ src/definition/metadata/IAppInfo.ts | 2 + src/definition/metadata/index.ts | 2 + src/server/AppManager.ts | 61 ++++++++------- src/server/ProxiedApp.ts | 4 + src/server/bridges/IListenerBridge.ts | 2 +- src/server/compiler/AppImplements.ts | 38 +--------- src/server/compiler/index.ts | 3 +- src/server/managers/AppListenerManager.ts | 75 ++++++++++++++++--- .../AppFabricationFulfillment.spec.ts | 5 +- tests/server/compiler/AppImplements.spec.ts | 4 +- .../managers/AppListenerManager.spec.ts | 2 +- 13 files changed, 157 insertions(+), 84 deletions(-) create mode 100644 src/definition/metadata/AppInterface.ts diff --git a/src/definition/AppStatus.ts b/src/definition/AppStatus.ts index 3990ce4cc..30e843e06 100644 --- a/src/definition/AppStatus.ts +++ b/src/definition/AppStatus.ts @@ -51,6 +51,13 @@ export class AppStatusUtilsDef { return false; } } + + public isError(status: AppStatus): boolean { + return [ + AppStatus.ERROR_DISABLED, + AppStatus.COMPILER_ERROR_DISABLED, + ].includes(status); + } } export const AppStatusUtils = new AppStatusUtilsDef(); diff --git a/src/definition/metadata/AppInterface.ts b/src/definition/metadata/AppInterface.ts new file mode 100644 index 000000000..42083112b --- /dev/null +++ b/src/definition/metadata/AppInterface.ts @@ -0,0 +1,36 @@ +export enum AppInterface { + // Messages + IPreMessageSentPrevent = 'IPreMessageSentPrevent', + IPreMessageSentExtend = 'IPreMessageSentExtend', + IPreMessageSentModify = 'IPreMessageSentModify', + IPostMessageSent = 'IPostMessageSent', + IPreMessageDeletePrevent = 'IPreMessageDeletePrevent', + IPostMessageDeleted = 'IPostMessageDeleted', + IPreMessageUpdatedPrevent = 'IPreMessageUpdatedPrevent', + IPreMessageUpdatedExtend = 'IPreMessageUpdatedExtend', + IPreMessageUpdatedModify = 'IPreMessageUpdatedModify', + IPostMessageUpdated = 'IPostMessageUpdated', + // Rooms + IPreRoomCreatePrevent = 'IPreRoomCreatePrevent', + IPreRoomCreateExtend = 'IPreRoomCreateExtend', + IPreRoomCreateModify = 'IPreRoomCreateModify', + IPostRoomCreate = 'IPostRoomCreate', + IPreRoomDeletePrevent = 'IPreRoomDeletePrevent', + IPostRoomDeleted = 'IPostRoomDeleted', + IPreRoomUserJoined = 'IPreRoomUserJoined', + IPostRoomUserJoined = 'IPostRoomUserJoined', + // External Components + IPostExternalComponentOpened = 'IPostExternalComponentOpened', + IPostExternalComponentClosed = 'IPostExternalComponentClosed', + // Blocks + IUIKitInteractionHandler = 'IUIKitInteractionHandler', + // Livechat + IPostLivechatRoomStarted = 'IPostLivechatRoomStarted', + IPostLivechatRoomClosed = 'IPostLivechatRoomClosed', + /** + * @deprecated please use the AppMethod.EXECUTE_POST_LIVECHAT_ROOM_CLOSED method + */ + ILivechatRoomClosedHandler = 'ILivechatRoomClosedHandler', + IPostLivechatAgentAssigned = 'IPostLivechatAgentAssigned', + IPostLivechatAgentUnassigned = 'IPostLivechatAgentUnassigned', +} diff --git a/src/definition/metadata/IAppInfo.ts b/src/definition/metadata/IAppInfo.ts index e48075951..ab8b26a64 100644 --- a/src/definition/metadata/IAppInfo.ts +++ b/src/definition/metadata/IAppInfo.ts @@ -1,3 +1,4 @@ +import { AppInterface } from './AppInterface'; import { IAppAuthorInfo } from './IAppAuthorInfo'; export interface IAppInfo { @@ -12,4 +13,5 @@ export interface IAppInfo { iconFile: string; /** Base64 string of the App's icon. */ iconFileContent?: string; + essentials?: [AppInterface]; } diff --git a/src/definition/metadata/index.ts b/src/definition/metadata/index.ts index f0c7e95b7..5894b2eca 100644 --- a/src/definition/metadata/index.ts +++ b/src/definition/metadata/index.ts @@ -3,6 +3,8 @@ import { IAppAuthorInfo } from './IAppAuthorInfo'; import { IAppInfo } from './IAppInfo'; import { RocketChatAssociationModel, RocketChatAssociationRecord } from './RocketChatAssociations'; +export * from './AppInterface'; + export { AppMethod, IAppAuthorInfo, diff --git a/src/server/AppManager.ts b/src/server/AppManager.ts index d8b84882c..f9135e983 100644 --- a/src/server/AppManager.ts +++ b/src/server/AppManager.ts @@ -222,9 +222,11 @@ export class AppManager { // Now let's enable the apps which were once enabled // but are not currently disabled. - for (const rl of this.apps.values()) { - if (!AppStatusUtils.isDisabled(rl.getStatus()) && AppStatusUtils.isEnabled(rl.getPreviousStatus())) { - await this.enableApp(items.get(rl.getID()), rl, true, rl.getPreviousStatus() === AppStatus.MANUALLY_ENABLED).catch(console.error); + for (const app of this.apps.values()) { + if (!AppStatusUtils.isDisabled(app.getStatus()) && AppStatusUtils.isEnabled(app.getPreviousStatus())) { + await this.enableApp(items.get(app.getID()), app, true, app.getPreviousStatus() === AppStatus.MANUALLY_ENABLED).catch(console.error); + } else if (!AppStatusUtils.isError(app.getStatus())) { + this.listenerManager.lockEssentialEvents(app); } } @@ -239,21 +241,18 @@ export class AppManager { return; } - for (const rl of this.apps.values()) { - if (AppStatusUtils.isDisabled(rl.getStatus())) { - continue; - } - - if (rl.getStatus() === AppStatus.INITIALIZED) { - this.listenerManager.unregisterListeners(rl); - this.commandManager.unregisterCommands(rl.getID()); - this.externalComponentManager.unregisterExternalComponents(rl.getID()); - this.apiManager.unregisterApis(rl.getID()); - this.accessorManager.purifyApp(rl.getID()); - continue; + for (const app of this.apps.values()) { + if (app.getStatus() === AppStatus.INITIALIZED) { + this.listenerManager.unregisterListeners(app); + this.commandManager.unregisterCommands(app.getID()); + this.externalComponentManager.unregisterExternalComponents(app.getID()); + this.apiManager.unregisterApis(app.getID()); + this.accessorManager.purifyApp(app.getID()); + } else if (!AppStatusUtils.isDisabled(app.getStatus())) { + await this.disable(app.getID(), isManual ? AppStatus.MANUALLY_DISABLED : AppStatus.DISABLED); } - await this.disable(rl.getID(), isManual ? AppStatus.MANUALLY_DISABLED : AppStatus.DISABLED); + this.listenerManager.releaseEssentialEvents(app); } // Remove all the apps from the system now that we have unloaded everything @@ -350,33 +349,34 @@ export class AppManager { throw new Error('Invalid disabled status'); } - const rl = this.apps.get(id); + const app = this.apps.get(id); - if (!rl) { + if (!app) { throw new Error(`No App by the id "${id}" exists.`); } - if (AppStatusUtils.isEnabled(rl.getStatus())) { - await rl.call(AppMethod.ONDISABLE, this.accessorManager.getConfigurationModify(rl.getID())) + if (AppStatusUtils.isEnabled(app.getStatus())) { + await app.call(AppMethod.ONDISABLE, this.accessorManager.getConfigurationModify(app.getID())) .catch((e) => console.warn('Error while disabling:', e)); } - this.listenerManager.unregisterListeners(rl); - this.commandManager.unregisterCommands(rl.getID()); - this.externalComponentManager.unregisterExternalComponents(rl.getID()); - this.apiManager.unregisterApis(rl.getID()); - this.accessorManager.purifyApp(rl.getID()); + this.listenerManager.unregisterListeners(app); + this.listenerManager.lockEssentialEvents(app); + this.commandManager.unregisterCommands(app.getID()); + this.externalComponentManager.unregisterExternalComponents(app.getID()); + this.apiManager.unregisterApis(app.getID()); + this.accessorManager.purifyApp(app.getID()); - await rl.setStatus(status, silent); + await app.setStatus(status, silent); const storageItem = await this.storage.retrieveOne(id); - rl.getStorageItem().marketplaceInfo = storageItem.marketplaceInfo; - await rl.validateLicense().catch(); + app.getStorageItem().marketplaceInfo = storageItem.marketplaceInfo; + await app.validateLicense().catch(); // This is async, but we don't care since it only updates in the database // and it should not mutate any properties we care about - storageItem.status = rl.getStatus(); + storageItem.status = app.getStatus(); await this.storage.update(storageItem).catch(); return true; @@ -462,6 +462,7 @@ export class AppManager { } this.listenerManager.unregisterListeners(app); + this.listenerManager.releaseEssentialEvents(app); this.commandManager.unregisterCommands(app.getID()); this.externalComponentManager.purgeExternalComponents(app.getID()); this.apiManager.unregisterApis(app.getID()); @@ -796,10 +797,12 @@ export class AppManager { this.externalComponentManager.registerExternalComponents(app.getID()); this.apiManager.registerApis(app.getID()); this.listenerManager.registerListeners(app); + this.listenerManager.releaseEssentialEvents(app); } else { this.commandManager.unregisterCommands(app.getID()); this.externalComponentManager.unregisterExternalComponents(app.getID()); this.apiManager.unregisterApis(app.getID()); + this.listenerManager.lockEssentialEvents(app); } if (saveToDb) { diff --git a/src/server/ProxiedApp.ts b/src/server/ProxiedApp.ts index 85e03095c..6d210bc85 100644 --- a/src/server/ProxiedApp.ts +++ b/src/server/ProxiedApp.ts @@ -154,6 +154,10 @@ export class ProxiedApp implements IApp { return this.app.getAccessors(); } + public getEssentials(): IAppInfo['essentials'] { + return this.getInfo().essentials; + } + public getLatestLicenseValidationResult(): AppLicenseValidationResult { return this.latestLicenseValidationResult; } diff --git a/src/server/bridges/IListenerBridge.ts b/src/server/bridges/IListenerBridge.ts index fbf103678..cce42858e 100644 --- a/src/server/bridges/IListenerBridge.ts +++ b/src/server/bridges/IListenerBridge.ts @@ -1,7 +1,7 @@ import { IMessage } from '../../definition/messages'; +import { AppInterface } from '../../definition/metadata'; import { IRoom } from '../../definition/rooms'; import { IUIKitIncomingInteraction } from '../../definition/uikit'; -import { AppInterface } from '../compiler'; export interface IListenerBridge { messageEvent(int: AppInterface, message: IMessage): Promise; diff --git a/src/server/compiler/AppImplements.ts b/src/server/compiler/AppImplements.ts index 1894ab38f..459138a33 100644 --- a/src/server/compiler/AppImplements.ts +++ b/src/server/compiler/AppImplements.ts @@ -1,42 +1,6 @@ +import { AppInterface } from '../../definition/metadata/AppInterface'; import { Utilities } from '../misc/Utilities'; -export enum AppInterface { - // Messages - IPreMessageSentPrevent = 'IPreMessageSentPrevent', - IPreMessageSentExtend = 'IPreMessageSentExtend', - IPreMessageSentModify = 'IPreMessageSentModify', - IPostMessageSent = 'IPostMessageSent', - IPreMessageDeletePrevent = 'IPreMessageDeletePrevent', - IPostMessageDeleted = 'IPostMessageDeleted', - IPreMessageUpdatedPrevent = 'IPreMessageUpdatedPrevent', - IPreMessageUpdatedExtend = 'IPreMessageUpdatedExtend', - IPreMessageUpdatedModify = 'IPreMessageUpdatedModify', - IPostMessageUpdated = 'IPostMessageUpdated', - // Rooms - IPreRoomCreatePrevent = 'IPreRoomCreatePrevent', - IPreRoomCreateExtend = 'IPreRoomCreateExtend', - IPreRoomCreateModify = 'IPreRoomCreateModify', - IPostRoomCreate = 'IPostRoomCreate', - IPreRoomDeletePrevent = 'IPreRoomDeletePrevent', - IPostRoomDeleted = 'IPostRoomDeleted', - IPreRoomUserJoined = 'IPreRoomUserJoined', - IPostRoomUserJoined = 'IPostRoomUserJoined', - // External Components - IPostExternalComponentOpened = 'IPostExternalComponentOpened', - IPostExternalComponentClosed = 'IPostExternalComponentClosed', - // Blocks - IUIKitInteractionHandler = 'IUIKitInteractionHandler', - // Livechat - IPostLivechatRoomStarted = 'IPostLivechatRoomStarted', - IPostLivechatRoomClosed = 'IPostLivechatRoomClosed', - /** - * @deprecated please use the AppMethod.EXECUTE_POST_LIVECHAT_ROOM_CLOSED method - */ - ILivechatRoomClosedHandler = 'ILivechatRoomClosedHandler', - IPostLivechatAgentAssigned = 'IPostLivechatAgentAssigned', - IPostLivechatAgentUnassigned = 'IPostLivechatAgentUnassigned', -} - export class AppImplements { private implemented: { [key: string]: boolean }; diff --git a/src/server/compiler/index.ts b/src/server/compiler/index.ts index 7e72478ab..4bd13fe1a 100644 --- a/src/server/compiler/index.ts +++ b/src/server/compiler/index.ts @@ -1,6 +1,6 @@ import { AppCompiler } from './AppCompiler'; import { AppFabricationFulfillment } from './AppFabricationFulfillment'; -import { AppImplements, AppInterface } from './AppImplements'; +import { AppImplements } from './AppImplements'; import { AppPackageParser } from './AppPackageParser'; import { ICompilerError } from './ICompilerError'; import { ICompilerFile } from './ICompilerFile'; @@ -11,7 +11,6 @@ export { AppCompiler, AppFabricationFulfillment, AppImplements, - AppInterface, AppPackageParser, ICompilerFile, ICompilerError, diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index 2d886a95f..a2014e05e 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -1,7 +1,8 @@ +import { AppsEngineException } from '../../definition/exceptions'; import { IExternalComponent } from '../../definition/externalComponent'; import { ILivechatEventContext, ILivechatRoom } from '../../definition/livechat'; import { IMessage } from '../../definition/messages'; -import { AppMethod } from '../../definition/metadata'; +import { AppInterface, AppMethod } from '../../definition/metadata'; import { IRoom, IRoomUserJoinedContext } from '../../definition/rooms'; import { IUIKitIncomingInteraction, IUIKitResponse, IUIKitView, UIKitIncomingInteractionType } from '../../definition/uikit'; import { @@ -16,32 +17,46 @@ import { import { IUser } from '../../definition/users'; import { MessageBuilder, MessageExtender, RoomBuilder, RoomExtender } from '../accessors'; import { AppManager } from '../AppManager'; -import { AppInterface } from '../compiler'; import { Message } from '../messages/Message'; import { Utilities } from '../misc/Utilities'; import { ProxiedApp } from '../ProxiedApp'; import { Room } from '../rooms/Room'; import { AppAccessorManager } from './AppAccessorManager'; +export class EssentialAppDisabledException extends AppsEngineException {} + export class AppListenerManager { private am: AppAccessorManager; private listeners: Map>; + /** + * Locked events are those who are listed in an app's + * "essentials" list but the app is disabled. + * + * They will throw a EssentialAppDisabledException upon call + */ + private lockedEvents: Map>; constructor(private readonly manager: AppManager) { this.am = manager.getAccessorManager(); this.listeners = new Map>(); + this.lockedEvents = new Map>(); - Object.keys(AppInterface).forEach((intt) => this.listeners.set(intt, new Array())); + Object.keys(AppInterface).forEach((intt) => { + this.listeners.set(intt, new Array()); + this.lockedEvents.set(intt, new Set()); + }); } public registerListeners(app: ProxiedApp): void { this.unregisterListeners(app); - const impleList = app.getImplementationList(); - for (const int in app.getImplementationList()) { - if (impleList[int]) { - this.listeners.get(int).push(app.getID()); + + Object.entries(app.getImplementationList()).forEach(([event, isImplemented]) => { + if (!isImplemented) { + return; } - } + + this.listeners.get(event).push(app.getID()); + }); } public unregisterListeners(app: ProxiedApp): void { @@ -53,6 +68,38 @@ export class AppListenerManager { }); } + public releaseEssentialEvents(app: ProxiedApp): void { + if (!app.getEssentials()) { + return; + } + + app.getEssentials().forEach((event) => { + const lockedEvent = this.lockedEvents.get(event); + + if (!lockedEvent) { + return; + } + + lockedEvent.delete(app.getID()); + }); + } + + public lockEssentialEvents(app: ProxiedApp): void { + if (!app.getEssentials()) { + return; + } + + app.getEssentials().forEach((event) => { + const lockedEvent = this.lockedEvents.get(event); + + if (!lockedEvent) { + return; + } + + lockedEvent.add(app.getID()); + }); + } + public getListeners(int: AppInterface): Array { const results = new Array(); @@ -63,8 +110,18 @@ export class AppListenerManager { return results; } + public isEventBlocked(event: AppInterface): boolean { + const lockedEventList = this.lockedEvents.get(event); + + return !!(lockedEventList && lockedEventList.size); + } + // tslint:disable-next-line public async executeListener(int: AppInterface, data: IMessage | IRoom | IUser | ILivechatRoom | IUIKitIncomingInteraction | IExternalComponent | ILivechatEventContext | IRoomUserJoinedContext): Promise { + if (this.isEventBlocked(int)) { + throw new EssentialAppDisabledException('There is one or more apps that are essential to this event but are disabled'); + } + switch (int) { // Messages case AppInterface.IPreMessageSentPrevent: @@ -137,7 +194,7 @@ export class AppListenerManager { this.executePostLivechatAgentUnassigned(data as ILivechatEventContext); return; default: - console.warn('Unimplemented (or invalid) AppInterface was just tried to execute.'); + console.warn('An invalid listener was called'); return; } } diff --git a/tests/server/compiler/AppFabricationFulfillment.spec.ts b/tests/server/compiler/AppFabricationFulfillment.spec.ts index a77a11c74..ea55b783b 100644 --- a/tests/server/compiler/AppFabricationFulfillment.spec.ts +++ b/tests/server/compiler/AppFabricationFulfillment.spec.ts @@ -1,10 +1,9 @@ import { Expect, Test } from 'alsatian'; import { App } from '../../../src/definition/App'; import { AppStatus } from '../../../src/definition/AppStatus'; -import { IAppInfo } from '../../../src/definition/metadata'; - +import { AppInterface, IAppInfo } from '../../../src/definition/metadata'; import { AppManager } from '../../../src/server/AppManager'; -import { AppFabricationFulfillment, AppInterface, ICompilerError } from '../../../src/server/compiler'; +import { AppFabricationFulfillment, ICompilerError } from '../../../src/server/compiler'; import { ProxiedApp } from '../../../src/server/ProxiedApp'; import { IAppStorageItem } from '../../../src/server/storage'; diff --git a/tests/server/compiler/AppImplements.spec.ts b/tests/server/compiler/AppImplements.spec.ts index bca227534..ee2e55647 100644 --- a/tests/server/compiler/AppImplements.spec.ts +++ b/tests/server/compiler/AppImplements.spec.ts @@ -1,6 +1,6 @@ import { Expect, Test } from 'alsatian'; - -import { AppImplements, AppInterface } from '../../../src/server/compiler'; +import { AppInterface } from '../../../src/definition/metadata'; +import { AppImplements } from '../../../src/server/compiler'; export class AppImplementsTestFixture { @Test() diff --git a/tests/server/managers/AppListenerManager.spec.ts b/tests/server/managers/AppListenerManager.spec.ts index 33e75e88c..45e23f76c 100644 --- a/tests/server/managers/AppListenerManager.spec.ts +++ b/tests/server/managers/AppListenerManager.spec.ts @@ -1,6 +1,6 @@ import { Expect, SetupFixture, Test } from 'alsatian'; +import { AppInterface } from '../../../src/definition/metadata'; import { AppManager } from '../../../src/server/AppManager'; -import { AppInterface } from '../../../src/server/compiler'; import { AppListenerManager } from '../../../src/server/managers'; import { ProxiedApp } from '../../../src/server/ProxiedApp'; From 7ad5ea94c033617b6f091be3aaac21b5e325cf2a Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 18 May 2020 15:17:30 -0300 Subject: [PATCH 09/12] Refactor exceptions --- .../exceptions/AppsEngineException.ts | 13 +++++++++++++ .../exceptions/EssentialAppDisabledException.ts | 17 +++++++++++++++++ .../exceptions/UserNotAllowedException.ts | 14 ++++++++++++++ src/definition/exceptions/index.ts | 4 +++- src/definition/rooms/IPreRoomUserJoined.ts | 3 --- src/server/managers/AppListenerManager.ts | 4 +--- 6 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 src/definition/exceptions/EssentialAppDisabledException.ts create mode 100644 src/definition/exceptions/UserNotAllowedException.ts diff --git a/src/definition/exceptions/AppsEngineException.ts b/src/definition/exceptions/AppsEngineException.ts index 7d34164cd..0496bc414 100644 --- a/src/definition/exceptions/AppsEngineException.ts +++ b/src/definition/exceptions/AppsEngineException.ts @@ -1 +1,14 @@ +/** + * The internal exception from the framework + * + * It's used to signal to the outside world that + * a _known_ exception has happened during the execution + * of the apps. + * + * It's the base exception for other known classes + * such as UserNotAllowedException, which is used + * to inform the host that an app identified + * that a user cannot perform some action, e.g. + * join a room + */ export class AppsEngineException extends Error {} diff --git a/src/definition/exceptions/EssentialAppDisabledException.ts b/src/definition/exceptions/EssentialAppDisabledException.ts new file mode 100644 index 000000000..be7d2dc1a --- /dev/null +++ b/src/definition/exceptions/EssentialAppDisabledException.ts @@ -0,0 +1,17 @@ +import { AppsEngineException } from '.'; + +/** + * This exception informs the host system that an + * app essential to the execution of a system action + * is disabled, so the action should be halted. + * + * Apps can register to be considered essential to + * the execution of internal events of the framework + * such as `IPreMessageSentPrevent`, `IPreRoomUserJoined`, + * etc. + * + * This is used interally by the framework and is not + * intended to be thrown manually by apps. + */ +export class EssentialAppDisabledException extends AppsEngineException { +} diff --git a/src/definition/exceptions/UserNotAllowedException.ts b/src/definition/exceptions/UserNotAllowedException.ts new file mode 100644 index 000000000..2b3dd4d0e --- /dev/null +++ b/src/definition/exceptions/UserNotAllowedException.ts @@ -0,0 +1,14 @@ +import { AppsEngineException } from '.'; + +/** + * This exception informs the host system that an + * app has determined that an user is not allowed + * to perform a specific action. + * + * Currently it is expected to be thrown by the + * following events: + * - IPreRoomCreatePrevent + * - IPreRoomUserJoined + */ +export class UserNotAllowedException extends AppsEngineException { +} diff --git a/src/definition/exceptions/index.ts b/src/definition/exceptions/index.ts index 92b2db2ad..cd378444e 100644 --- a/src/definition/exceptions/index.ts +++ b/src/definition/exceptions/index.ts @@ -1 +1,3 @@ -export { AppsEngineException } from './AppsEngineException'; +export * from './AppsEngineException'; +export * from './EssentialAppDisabledException'; +export * from './UserNotAllowedException'; diff --git a/src/definition/rooms/IPreRoomUserJoined.ts b/src/definition/rooms/IPreRoomUserJoined.ts index d28165a76..f6607b21d 100644 --- a/src/definition/rooms/IPreRoomUserJoined.ts +++ b/src/definition/rooms/IPreRoomUserJoined.ts @@ -1,9 +1,6 @@ import { IHttp, IPersistence, IRead } from '../accessors'; -import { AppsEngineException } from '../exceptions'; import { IRoomUserJoinedContext } from './IRoomUserJoinedContext'; export interface IPreRoomUserJoined { executePreRoomUserJoined(context: IRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; } - -export class UserNotAllowedJoinException extends AppsEngineException {} diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index a2014e05e..242d05e8f 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -1,4 +1,4 @@ -import { AppsEngineException } from '../../definition/exceptions'; +import { EssentialAppDisabledException } from '../../definition/exceptions'; import { IExternalComponent } from '../../definition/externalComponent'; import { ILivechatEventContext, ILivechatRoom } from '../../definition/livechat'; import { IMessage } from '../../definition/messages'; @@ -23,8 +23,6 @@ import { ProxiedApp } from '../ProxiedApp'; import { Room } from '../rooms/Room'; import { AppAccessorManager } from './AppAccessorManager'; -export class EssentialAppDisabledException extends AppsEngineException {} - export class AppListenerManager { private am: AppAccessorManager; private listeners: Map>; From f62d402550344fbf5f85075a18a901cfbd14442a Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 18 May 2020 15:33:55 -0300 Subject: [PATCH 10/12] Add comments to the new interfaces --- src/definition/rooms/IPostRoomUserJoined.ts | 13 +++++++++++++ src/definition/rooms/IPreRoomUserJoined.ts | 12 ++++++++++++ src/definition/rooms/IRoomUserJoinedContext.ts | 15 +++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/definition/rooms/IPostRoomUserJoined.ts b/src/definition/rooms/IPostRoomUserJoined.ts index cacd5c64c..f05308590 100644 --- a/src/definition/rooms/IPostRoomUserJoined.ts +++ b/src/definition/rooms/IPostRoomUserJoined.ts @@ -1,6 +1,19 @@ import { IHttp, IPersistence, IRead } from '../accessors'; import { IRoomUserJoinedContext } from './IRoomUserJoinedContext'; +/** + * Event interface that allows an app to + * register as a handler of the `IPostRoomUserJoined` + * event + * + * This event is triggered after an user succcessfully joined + * a room. + * + * This event does not allow an app to prevent any action from + * happening. For that, see its "pre counterpart(s)": + * + * - IPreRoomUserJoined + */ export interface IPostRoomUserJoined { executePostRoomUserJoined(context: IRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; } diff --git a/src/definition/rooms/IPreRoomUserJoined.ts b/src/definition/rooms/IPreRoomUserJoined.ts index f6607b21d..525332553 100644 --- a/src/definition/rooms/IPreRoomUserJoined.ts +++ b/src/definition/rooms/IPreRoomUserJoined.ts @@ -1,6 +1,18 @@ import { IHttp, IPersistence, IRead } from '../accessors'; import { IRoomUserJoinedContext } from './IRoomUserJoinedContext'; +/** + * Event interface that allows an app to + * register as a handler of the `IPreRoomUserJoined` + * event + * + * This event is triggered prior to an user succcessfully + * joining a room. To prevent the user from executing + * such action, an app should throw the `UserNotAllowedException`. + * + * This event is not triggered before a room has been created. For that, + * check the `IPreRoomCreate` events + */ export interface IPreRoomUserJoined { executePreRoomUserJoined(context: IRoomUserJoinedContext, read: IRead, http: IHttp, persistence: IPersistence): Promise; } diff --git a/src/definition/rooms/IRoomUserJoinedContext.ts b/src/definition/rooms/IRoomUserJoinedContext.ts index 19c76572b..c9d827d5b 100644 --- a/src/definition/rooms/IRoomUserJoinedContext.ts +++ b/src/definition/rooms/IRoomUserJoinedContext.ts @@ -1,8 +1,23 @@ import { IUser } from '../users'; import { IRoom } from './IRoom'; +/** + * The context of execution for the following events: + * - IPreRoomUserJoined + * - IPostRoomUserJoined + */ export interface IRoomUserJoinedContext { + /** + * The user that is being added to the room + */ joiningUser: IUser; + /** + * The room to which the user is being added + */ room: IRoom; + /** + * The user that has invited `joiningUser` to `room`, + * if any. + */ invitingUser?: IUser; } From b90d4437031fd96b914e897e2080b77f425df7bf Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 18 May 2020 19:05:04 -0300 Subject: [PATCH 11/12] Change invitingUser to inviter --- src/definition/rooms/IRoomUserJoinedContext.ts | 2 +- src/server/managers/AppListenerManager.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/definition/rooms/IRoomUserJoinedContext.ts b/src/definition/rooms/IRoomUserJoinedContext.ts index c9d827d5b..c76e9d9ba 100644 --- a/src/definition/rooms/IRoomUserJoinedContext.ts +++ b/src/definition/rooms/IRoomUserJoinedContext.ts @@ -19,5 +19,5 @@ export interface IRoomUserJoinedContext { * The user that has invited `joiningUser` to `room`, * if any. */ - invitingUser?: IUser; + inviter?: IUser; } diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index 242d05e8f..fb1da07c5 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -683,8 +683,8 @@ export class AppListenerManager { data.room = new Room(Utilities.deepFreeze(data.room), this.manager); Utilities.deepFreeze(data.joiningUser); - if (data.invitingUser) { - Utilities.deepFreeze(data.invitingUser); + if (data.inviter) { + Utilities.deepFreeze(data.inviter); } for (const appId of this.listeners.get(AppInterface.IPreRoomUserJoined)) { @@ -707,8 +707,8 @@ export class AppListenerManager { data.room = new Room(Utilities.deepFreeze(data.room), this.manager); Utilities.deepFreeze(data.joiningUser); - if (data.invitingUser) { - Utilities.deepFreeze(data.invitingUser); + if (data.inviter) { + Utilities.deepFreeze(data.inviter); } for (const appId of this.listeners.get(AppInterface.IPostRoomUserJoined)) { From ed39c17d548c18cb2a2ff3d3d80b3f8708ff9812 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 18 May 2020 21:26:13 -0300 Subject: [PATCH 12/12] Switch typing style --- src/definition/metadata/IAppInfo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/definition/metadata/IAppInfo.ts b/src/definition/metadata/IAppInfo.ts index ab8b26a64..3fa484869 100644 --- a/src/definition/metadata/IAppInfo.ts +++ b/src/definition/metadata/IAppInfo.ts @@ -13,5 +13,5 @@ export interface IAppInfo { iconFile: string; /** Base64 string of the App's icon. */ iconFileContent?: string; - essentials?: [AppInterface]; + essentials?: Array; }