Skip to content

Commit

Permalink
feat: SMTP OTP bridge (#790)
Browse files Browse the repository at this point in the history
Co-authored-by: Douglas Gubert <[email protected]>
Co-authored-by: Rafael Tapia <[email protected]>
  • Loading branch information
3 people committed Aug 27, 2024
1 parent 7c81af6 commit 737d26a
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 0 deletions.
21 changes: 21 additions & 0 deletions deno-runtime/lib/accessors/modify/ModifyCreator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { IModifyCreator } from '@rocket.chat/apps-engine/definition/accessors/IModifyCreator.ts';
import type { IUploadCreator } from '@rocket.chat/apps-engine/definition/accessors/IUploadCreator.ts';
import type { IEmailCreator } from '@rocket.chat/apps-engine/definition/accessors/IEmailCreator.ts';
import type { ILivechatCreator } from '@rocket.chat/apps-engine/definition/accessors/ILivechatCreator.ts';
import type { IMessage } from '@rocket.chat/apps-engine/definition/messages/IMessage.ts';
import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms/IRoom.ts';
Expand Down Expand Up @@ -85,6 +86,26 @@ export class ModifyCreator implements IModifyCreator {
) as IUploadCreator;
}

getEmailCreator(): IEmailCreator {
return new Proxy(
{ __kind: 'getEmailCreator' },
{
get: (_target: unknown, prop: string) =>
(...params: unknown[]) =>
prop === 'toJSON'
? {}
: this.senderFn({
method: `accessor:getModifier:getCreator:getEmailCreator:${prop}`,
params
})
.then((response) => response.result)
.catch((err) => {
throw new Error(err.error);
}),
}
)
}

getBlockBuilder() {
return new BlockBuilder();
}
Expand Down
10 changes: 10 additions & 0 deletions src/definition/accessors/IEmailCreator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { IEmail } from '../email';

export interface IEmailCreator {
/**
* Sends an email through Rocket.Chat
*
* @param email the email data
*/
send(email: IEmail): Promise<void>;
}
6 changes: 6 additions & 0 deletions src/definition/accessors/IModifyCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { IRoomBuilder } from './IRoomBuilder';
import type { IUploadCreator } from './IUploadCreator';
import type { IUserBuilder } from './IUserBuilder';
import type { IVideoConferenceBuilder } from './IVideoConferenceBuilder';
import type { IEmailCreator } from './IEmailCreator';

export interface IModifyCreator {
/**
Expand All @@ -25,6 +26,11 @@ export interface IModifyCreator {
*/
getUploadCreator(): IUploadCreator;

/**
* Gets the creator object responsible for email sending
*/
getEmailCreator(): IEmailCreator;

/**
* @deprecated please prefer the rocket.chat/ui-kit components
*
Expand Down
9 changes: 9 additions & 0 deletions src/definition/email/IEmail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface IEmail {
to: string | string[];
from: string;
replyTo?: string;
subject: string;
html?: string;
text?: string;
headers?: string;
}
1 change: 1 addition & 0 deletions src/definition/email/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './IEmailDescriptor';
export * from './IPreEmailSent';
export * from './IPreEmailSentContext';
export * from './IEmail';
11 changes: 11 additions & 0 deletions src/server/accessors/EmailCreator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { AppBridges } from '../bridges';
import type { IEmailCreator } from '../../definition/accessors/IEmailCreator';
import type { IEmail } from '../../definition/email';

export class EmailCreator implements IEmailCreator {
constructor(private readonly bridges: AppBridges, private readonly appId: string) {}

public async send(email: IEmail): Promise<void> {
return this.bridges.getEmailBridge().doSendEmail(email, this.appId);
}
}
9 changes: 9 additions & 0 deletions src/server/accessors/ModifyCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@ import { RoomBuilder } from './RoomBuilder';
import { UploadCreator } from './UploadCreator';
import { UserBuilder } from './UserBuilder';
import { VideoConferenceBuilder } from './VideoConferenceBuilder';
import { EmailCreator } from './EmailCreator';
import type { IEmailCreator } from '../../definition/accessors/IEmailCreator';

export class ModifyCreator implements IModifyCreator {
private livechatCreator: LivechatCreator;

private uploadCreator: UploadCreator;

private emailCreator: EmailCreator;

constructor(private readonly bridges: AppBridges, private readonly appId: string) {
this.livechatCreator = new LivechatCreator(bridges, appId);
this.uploadCreator = new UploadCreator(bridges, appId);
this.emailCreator = new EmailCreator(bridges, appId);
}

public getLivechatCreator(): ILivechatCreator {
Expand All @@ -47,6 +52,10 @@ export class ModifyCreator implements IModifyCreator {
return this.uploadCreator;
}

public getEmailCreator(): IEmailCreator {
return this.emailCreator;
}

/**
* @deprecated please prefer the rocket.chat/ui-kit components
*/
Expand Down
4 changes: 4 additions & 0 deletions src/server/bridges/AppBridges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { RoleBridge } from './RoleBridge';
import type { RoomBridge } from './RoomBridge';
import type { SchedulerBridge } from './SchedulerBridge';
import type { ServerSettingBridge } from './ServerSettingBridge';
import type { EmailBridge } from './EmailBridge';
import type { ThreadBridge } from './ThreadBridge';
import type { UiInteractionBridge } from './UiInteractionBridge';
import type { UploadBridge } from './UploadBridge';
Expand All @@ -37,6 +38,7 @@ export type Bridge =
| RoomBridge
| IInternalBridge
| ServerSettingBridge
| EmailBridge
| UploadBridge
| UserBridge
| UiInteractionBridge
Expand Down Expand Up @@ -77,6 +79,8 @@ export abstract class AppBridges {

public abstract getUploadBridge(): UploadBridge;

public abstract getEmailBridge(): EmailBridge;

public abstract getUserBridge(): UserBridge;

public abstract getUiInteractionBridge(): UiInteractionBridge;
Expand Down
30 changes: 30 additions & 0 deletions src/server/bridges/EmailBridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { IEmail } from '../../definition/email';
import { PermissionDeniedError } from '../errors/PermissionDeniedError';
import { AppPermissionManager } from '../managers/AppPermissionManager';
import { AppPermissions } from '../permissions/AppPermissions';
import { BaseBridge } from './BaseBridge';

export abstract class EmailBridge extends BaseBridge {
public async doSendEmail(email: IEmail, appId: string): Promise<void> {
if (this.hasWritePermission(appId)) {
return this.sendEmail(email, appId);
}
}

protected abstract sendEmail(email: IEmail, appId: string): Promise<void>;

private hasWritePermission(appId: string): boolean {
if (AppPermissionManager.hasPermission(appId, AppPermissions.email.send)) {
return true;
}

AppPermissionManager.notifyAboutError(
new PermissionDeniedError({
appId,
missingPermissions: [AppPermissions.email.send],
}),
);

return false;
}
}
2 changes: 2 additions & 0 deletions src/server/bridges/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AppBridges } from './AppBridges';
import { AppDetailChangesBridge } from './AppDetailChangesBridge';
import { CloudWorkspaceBridge } from './CloudWorkspaceBridge';
import { CommandBridge } from './CommandBridge';
import { EmailBridge } from './EmailBridge';
import { EnvironmentalVariableBridge } from './EnvironmentalVariableBridge';
import { HttpBridge, IHttpBridgeRequestInfo } from './HttpBridge';
import { IInternalBridge } from './IInternalBridge';
Expand Down Expand Up @@ -40,6 +41,7 @@ export {
ServerSettingBridge,
UserBridge,
UploadBridge,
EmailBridge,
UiInteractionBridge,
SchedulerBridge,
AppBridges,
Expand Down
3 changes: 3 additions & 0 deletions src/server/permissions/AppPermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const AppPermissions = {
read: { name: 'upload.read' },
write: { name: 'upload.write' },
},
email: {
send: { name: 'email.send' },
},
ui: {
interaction: { name: 'ui.interact' },
registerButtons: { name: 'ui.registerButtons' },
Expand Down
9 changes: 9 additions & 0 deletions tests/test-data/bridges/appBridges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { AppBridges } from '../../../src/server/bridges';
import type { CloudWorkspaceBridge } from '../../../src/server/bridges/CloudWorkspaceBridge';
import type { IInternalFederationBridge } from '../../../src/server/bridges/IInternalFederationBridge';
import type { OAuthAppsBridge } from '../../../src/server/bridges/OAuthAppsBridge';
import type { EmailBridge } from '../../../src/server/bridges/EmailBridge';
import type { ThreadBridge } from '../../../src/server/bridges/ThreadBridge';
import { TestsActivationBridge } from './activationBridge';
import { TestsApiBridge } from './apiBridge';
Expand All @@ -41,6 +42,7 @@ import { TestsRoleBridge } from './roleBridge';
import { TestsRoomBridge } from './roomBridge';
import { TestSchedulerBridge } from './schedulerBridge';
import { TestsServerSettingBridge } from './serverSettingBridge';
import { TestsEmailBridge } from './emailBridge';
import { TestsThreadBridge } from './threadBridge';
import { TestsUiIntegrationBridge } from './uiIntegrationBridge';
import { TestUploadBridge } from './uploadBridge';
Expand Down Expand Up @@ -80,6 +82,8 @@ export class TestsAppBridges extends AppBridges {

private readonly uploadBridge: TestUploadBridge;

private readonly emailBridge: EmailBridge;

private readonly uiIntegrationBridge: TestsUiIntegrationBridge;

private readonly schedulerBridge: TestSchedulerBridge;
Expand Down Expand Up @@ -119,6 +123,7 @@ export class TestsAppBridges extends AppBridges {
this.oauthBridge = new TestOAuthAppsBridge();
this.internalFederationBridge = new TestsInternalFederationBridge();
this.threadBridge = new TestsThreadBridge();
this.emailBridge = new TestsEmailBridge();
}

public getCommandBridge(): TestsCommandBridge {
Expand Down Expand Up @@ -189,6 +194,10 @@ export class TestsAppBridges extends AppBridges {
return this.livechatBridge;
}

public getEmailBridge(): EmailBridge {
return this.emailBridge;
}

public getUploadBridge(): UploadBridge {
return this.uploadBridge;
}
Expand Down
8 changes: 8 additions & 0 deletions tests/test-data/bridges/emailBridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { IEmail } from '../../../src/definition/email';
import { EmailBridge } from '../../../src/server/bridges/EmailBridge';

export class TestsEmailBridge extends EmailBridge {
protected sendEmail(email: IEmail, appId: string): Promise<void> {
throw new Error('Method not implemented.');
}
}

0 comments on commit 737d26a

Please sign in to comment.