Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions lib/core/mail/SendGridMailService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import sendGridMail, { MailService as SendGridClient } from '@sendgrid/mail';

export interface MailerConfig {
from?: string;
sendgrid: {
api_key: string;
sandbox_mode?: boolean;
delete_template_id: string;
};
}

export interface MailService {
sendDeleteUserMail: (sendTo: string, redirect: string) => Promise<void>;
}

const DEFAULT_FROM = 'activate@internxt.com';

export class SendGridMailService implements MailService {
constructor(
private config: MailerConfig,
private client: Pick<SendGridClient, 'setApiKey' | 'send'> = sendGridMail
) {
this.client.setApiKey(config.sendgrid.api_key);
}

async sendDeleteUserMail(sendTo: string, redirect: string): Promise<void> {
const { from, sendgrid } = this.config;

await this.client.send({
to: sendTo,
from: {
email: from || DEFAULT_FROM,
name: 'Internxt',
},
subject: '',
text: 'deactivate acount',
html: 'deactivate acount',
personalizations: [
{
to: [{ email: sendTo }],
dynamicTemplateData: {
email: sendTo,
deletetion_url: redirect,
},
},
],
templateId: sendgrid.delete_template_id,
mailSettings: {
sandboxMode: {
enable: sendgrid.sandbox_mode || false,
},
},
}, false);
}
}
3 changes: 3 additions & 0 deletions lib/core/users/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export interface User {
preferences?: {
dnt: boolean;
};
configuration?: {
disableDeactivation?: boolean;
};
created?: Date;
// Following attributes are deleted in toObject() from storj-service-storage-models:
// pendingHashPass?: string | null;
Expand Down
31 changes: 22 additions & 9 deletions lib/core/users/usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createHash, randomBytes } from 'crypto';
import { UsersRepository } from './Repository';
import { BucketsRepository } from '../buckets/Repository';
import { MailUsecase } from '../mail/usecase';
import { MailService } from '../mail/SendGridMailService';
import { EventBus, EventBusEvents } from '../../server/eventBus';
import { FramesRepository } from '../frames/Repository';
import { BasicUser, User } from './User';
Expand Down Expand Up @@ -66,13 +67,22 @@ export class ResetPasswordImpersonationError extends Error {
}
}

export class UserDeactivationDisabledError extends Error {
constructor() {
super('This account has the deactivation disabled');

Object.setPrototypeOf(this, UserDeactivationDisabledError.prototype);
}
}

export class UsersUsecase {
constructor(
private usersRepository: UsersRepository,
private framesRepository: FramesRepository,
private bucketsRepository: BucketsRepository,
private mailUsecase: MailUsecase,
private eventBus: EventBus
private eventBus: EventBus,
private mailService: MailService
) {}

async getUserUsage(uuid: User['uuid']): Promise<UserSpaceSnapshot> {
Expand Down Expand Up @@ -227,22 +237,25 @@ export class UsersUsecase {
return user;
}

async requestUserDestroy(userId: string, deactivator: string, redirect: string) {
async requestUserDestroy(
userId: string,
deactivator: string,
redirect: string
): Promise<Pick<User, 'email' | 'uuid'>> {
const user = await this.usersRepository.findById(userId);

if (!user) {
throw new UserNotFoundError(userId);
}

if (user.configuration?.disableDeactivation) {
throw new UserDeactivationDisabledError();
}

await this.mailService.sendDeleteUserMail(user.email, redirect);
await this.usersRepository.updateById(user.id, { deactivator });

this.eventBus.emit(EventBusEvents.UserDestroyRequest, {
userRequestingDestroyEmail: user.email,
mailParams: {
deactivator,
redirect
}
});
return { email: user.email, uuid: user.uuid };
}

async confirmDestroyUser(deactivator: string): Promise<BasicUser> {
Expand Down
5 changes: 4 additions & 1 deletion lib/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const { ErrorHandlerFactory: errorHandler } = require('./server/middleware/error
const Server = require('./server');
const pow = require('./server/middleware/pow');
const Mailer = require('inxt-service-mailer');
const { SendGridMailService } = require('./core/mail/SendGridMailService');
const log = require('./logger');
const NetworkMessageQueue = require('./server/queues/networkQueue');
const { bindNewRoutes } = require('./server/http');
Expand Down Expand Up @@ -119,6 +120,7 @@ Engine.prototype.start = function (callback) {
);

this.mailer = new Mailer(this._config.mailer);
this.mailService = new SendGridMailService(this._config.mailer);
this.redis = require('redis').createClient(this._config.redis);
this.redis.on('ready', () => {
log.info('connected to redis');
Expand Down Expand Up @@ -266,7 +268,8 @@ Engine.prototype._configureApp = function () {
},
log,
this.networkQueue,
this.notifications
this.notifications,
this.mailService
);

return app;
Expand Down
42 changes: 0 additions & 42 deletions lib/server/eventBus.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { EventEmitter } from 'events';
import { Logger } from 'winston';
import { MailUsecase } from '../core/mail/usecase';
import { Notifications } from './notifications';
import { User } from '../core/users/User';

Expand All @@ -9,7 +8,6 @@ const { trackUserActivated } = require('../analytics');
export enum EventBusEvents {
UserCreationStarts = 'user-creation-starts',
UserCreationEnds = 'user-creation-ends',
UserDestroyRequest = 'user-destroy-request',
FilesBulkDeleteFailed = 'files-bulk-delete-failed',
UserStorageChanged = 'user-storage-changed',
}
Expand All @@ -23,14 +21,6 @@ interface UserCreationEndsPayload {
uuid: User['id'];
}

interface UserDestroyRequestPayload {
userRequestingDestroyEmail: User['email'];
mailParams: {
deactivator: string;
redirect: string;
};
}

interface FilesBulkDeleteFailedPayload {
err: Error;
fileIds: string;
Expand All @@ -44,7 +34,6 @@ export interface UserStorageChangedPayload {
export class EventBus extends EventEmitter {
constructor(
logger: Logger,
mailer: MailUsecase,
notifications: Notifications
) {
super();
Expand All @@ -65,37 +54,6 @@ export class EventBus extends EventEmitter {
}
);

this.on(
EventBusEvents.UserDestroyRequest,
(payload: UserDestroyRequestPayload) => {
logger.info(
'User %s requesting destroy',
payload.userRequestingDestroyEmail
);

mailer
.sendDeleteUserMail(
payload.userRequestingDestroyEmail,
payload.mailParams.deactivator,
payload.mailParams.redirect
)
.then(() => {
logger.info(
'User destroy email sent to %s',
payload.userRequestingDestroyEmail
);
})
.catch((err) => {
logger.error(
'Error sending user destroy email to %s: %s. %s',
payload.userRequestingDestroyEmail,
err.message,
err.stack
);
});
}
);

this.on(
EventBusEvents.FilesBulkDeleteFailed,
({ err, fileIds }: FilesBulkDeleteFailedPayload) => {
Expand Down
9 changes: 6 additions & 3 deletions lib/server/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BucketsRepository } from "../../core/buckets/Repository";
import { UsersRepository } from "../../core/users/Repository";

import { Mailer, MailUsecase, Profile, SendGridMailUsecase } from "../../core/mail/usecase";
import { MailService } from "../../core/mail/SendGridMailService";
import { createUsersHTTPRouter } from "./users";
import { HTTPUsersController } from "./users/controller";
import { EventBus } from "../eventBus";
Expand Down Expand Up @@ -57,7 +58,8 @@ export function bindNewRoutes(
profile: Profile,
log: Logger,
networkQueue: any,
notifications: Notifications
notifications: Notifications,
mailService: MailService
): void {
const { models } = storage;

Expand All @@ -73,14 +75,15 @@ export function bindNewRoutes(
const fileStateRepository: FileStateRepository = new MongoDBFileStateRepository(models.FileState);

const mailUsecase: MailUsecase = new SendGridMailUsecase(mailer, profile);
const eventBus = new EventBus(log, mailUsecase, notifications);
const eventBus = new EventBus(log, notifications);

const usersUsecase = new UsersUsecase(
usersRepository,
framesRepository,
bucketsRepository,
mailUsecase,
eventBus
eventBus,
mailService
);

const gatewayUsecase = new GatewayUsecase(
Expand Down
4 changes: 4 additions & 0 deletions lib/server/http/users/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Logger } from 'winston';

import {
InvalidDataFormatError,
UserDeactivationDisabledError,
UserNotFoundError,
UsersUsecase
} from '../../../core';
Expand Down Expand Up @@ -104,6 +105,9 @@ export class HTTPUsersController {
if (err instanceof UserNotFoundError) {
return res.status(404).send({ error: err.message });
}
if (err instanceof UserDeactivationDisabledError) {
return res.status(409).send({ error: err.message });
}
this.logger.error(
'Error requesting user destroy for user %s: %s. %s',
(req as AuthorizedRequest<any>).user._id,
Expand Down
10 changes: 2 additions & 8 deletions tests/lib/core/gateway/usecase.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { stub, restore } from 'sinon';
import { createLogger } from 'winston';
import Mailer from 'inxt-service-mailer';

import { BucketEntriesRepository } from '../../../../lib/core/bucketEntries/Repository';
import { FramesRepository } from '../../../../lib/core/frames/Repository';
Expand All @@ -20,7 +19,6 @@ import {
} from './fixtures';
import { Pointer } from '../../../../lib/core/pointers/Pointer';
import { EventBus, EventBusEvents } from '../../../../lib/server/eventBus';
import { MailUsecase, SendGridMailUsecase } from '../../../../lib/core/mail/usecase';
import { ShardsRepository } from '../../../../lib/core/shards/Repository';
import { MongoDBShardsRepository } from '../../../../lib/core/shards/MongoDBShardsRepository';
import { ContactsRepository } from '../../../../lib/core/contacts/Repository';
Expand All @@ -33,18 +31,14 @@ let pointersRepository: PointersRepository = new MongoDBPointersRepository({});
let mirrorsRepository: MirrorsRepository = new MongoDBMirrorsRepository({});
let shardsRepository: ShardsRepository = new MongoDBShardsRepository({});
let contactsRepository: ContactsRepository = new MongoDBContactsRepository({});
let mailUsecase: MailUsecase = new SendGridMailUsecase({} as Mailer, {
host: '',
protocol: 'http:',
});

let logger = createLogger({
silent: true
});

let notifications = new Notifications('','');

let eventBus = new EventBus(logger, mailUsecase, notifications);
let eventBus = new EventBus(logger, notifications);

let usecase = new GatewayUsecase(
bucketEntriesRepository,
Expand All @@ -63,7 +57,7 @@ beforeEach(() => {
mirrorsRepository = new MongoDBMirrorsRepository({});
shardsRepository = new MongoDBShardsRepository({});
contactsRepository = new MongoDBContactsRepository({});
eventBus = new EventBus(logger, mailUsecase, notifications);
eventBus = new EventBus(logger, notifications);

usecase = new GatewayUsecase(
bucketEntriesRepository,
Expand Down
Loading
Loading