Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SMTP OTP bridge #790

Merged
merged 15 commits into from
Aug 27, 2024
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.');
}
}
Loading