Skip to content
This repository has been archived by the owner on Aug 11, 2024. It is now read-only.

Commit

Permalink
feat(MFA): Restructures for reset 2FA flow
Browse files Browse the repository at this point in the history
  • Loading branch information
JumpLink committed Nov 10, 2023
2 parents 8f8bd1e + dd3b81a commit 0d1128a
Show file tree
Hide file tree
Showing 52 changed files with 305 additions and 215 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ docker compose up -d
Whenever you make changes to an object that is mapped into the database, you have to use `typeORM` and create a new migration file. Make sure the database container is running and then:

```
docker compose run app npm run typeorm migration:generate -- -n <MigrationName>
docker compose run -u root app npm run typeorm migration:generate -- -n <MigrationName>
npm run build
docker compose run app npm run typeorm migration:run
```
Expand Down
1 change: 1 addition & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require("module-alias/register");
const autoprefixer = require("autoprefixer");
const gulp = require("gulp");
const postcss = require("gulp-postcss");
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@
"@apps": "./built/apps",
"@config": "./built/config/config.js",
"@locale": "./built/locales/current.js",
"@models": "./built/models"
"@models": "./built/models",
"@type": "./built/type",
"@enums": "./built/enums"
}
}
4 changes: 1 addition & 3 deletions src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import "reflect-metadata";
import { RoleType } from "@beabee/beabee-common";
import cookie from "cookie-parser";
import cors from "cors";
import crypto from "crypto";
import express, { ErrorRequestHandler, Request } from "express";
import express, { ErrorRequestHandler } from "express";
import {
Action,
HttpError,
InternalServerError,
NotFoundError,
useExpressServer
} from "routing-controllers";
import { getRepository } from "typeorm";

import { ApiKeyController } from "./controllers/ApiKeyController";
import { AuthController } from "./controllers/AuthController";
Expand Down
8 changes: 3 additions & 5 deletions src/api/controllers/AuthController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@ import { UnauthorizedError } from "../errors/UnauthorizedError";
import passport from "@core/lib/passport";

import ContactsService from "@core/services/ContactsService";
import ContactMfaService from "@core/services/ContactMfaService";

import Contact from "@models/Contact";
import ContactRole from "@models/ContactRole";

import { login } from "@api/utils";

import {
LOGIN_CODES,
PassportLoginInfo
} from "@api/data/ContactData/interface";
import { PassportLoginInfo } from "@type/passport-login-info";

import { LOGIN_CODES } from "@enums/login-codes";

import config from "@config";

Expand Down
15 changes: 10 additions & 5 deletions src/api/controllers/ContactController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,8 @@ import {
CreateContactData,
DeleteContactMfaData,
fetchPaginatedContacts,
GetContactData,
GetContactQuery,
GetContactRoleData,
GetContactsQuery,
GetContactWith,
UpdateContactRoleData,
UpdateContactData,
exportContacts,
Expand All @@ -61,6 +58,9 @@ import {
CreateContactMfaData,
GetContactMfaData
} from "@api/data/ContactData";
import { GetContactData } from "@type/get-contact-data";
import { GetContactRoleData } from "@type/get-contact-role-data";
import { GetContactWith } from "@enums/get-contact-with";
import {
CompleteJoinFlowData,
StartJoinFlowData
Expand All @@ -77,7 +77,6 @@ import {
GetExportQuery
} from "@api/data/PaginatedData";
import { GetPaymentData, GetPaymentsQuery } from "@api/data/PaymentData";
import { LOGIN_CODES } from "@api/data/ContactData/interface";

import PartialBody from "@api/decorators/PartialBody";
import CantUpdateContribution from "@api/errors/CantUpdateContribution";
Expand Down Expand Up @@ -313,7 +312,13 @@ export class ContactController {
@Body() data: DeleteContactMfaData,
@Params() { id }: { id: string }
): Promise<void> {
await ContactMfaService.delete(target, id, data);
if (id === "me") {
await ContactMfaService.deleteSecure(target, data);
} else {
// It's secure to call this unsecure method here because the user is an admin,
// this is checked in the `@TargetUser()` decorator
await ContactMfaService.deleteUnsecure(target);
}
}

@OnUndefined(204)
Expand Down
16 changes: 7 additions & 9 deletions src/api/controllers/ResetDeviceController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ import EmailService from "@core/services/EmailService";
import AuthService from "@core/services/AuthService";
import ContactMfaService from "@core/services/ContactMfaService";

import ResetPasswordFlow from "@models/ResetPasswordFlow";
import ResetSecurityFlow from "@models/ResetSecurityFlow";

import { login } from "@api/utils";
import { UUIDParam } from "@api/data";
import {
CreateResetDeviceData,
UpdateResetDeviceData
} from "@api/data/ResetDeviceData";
import { ContactMfaType } from "@api/data/ContactData/interface";
import UnauthorizedError from "@api/errors/UnauthorizedError";

@JsonController("/reset-device")
export class ResetDeviceController {
@OnUndefined(204)
@Post()
async create(@Body() data: CreateResetDeviceData): Promise<void> {
// TODO: Create ResetSecurityFlowService
const contact = await ContactsService.findOne({ email: data.email });
if (!contact) {
return;
Expand All @@ -41,7 +41,7 @@ export class ResetDeviceController {

// TODO: Check if reset password flow already exists, if so throw error

const rpFlow = await getRepository(ResetPasswordFlow).save({ contact });
const rpFlow = await getRepository(ResetSecurityFlow).save({ contact });
await EmailService.sendTemplateToContact("reset-device", contact, {
rpLink: data.resetUrl + "/" + rpFlow.id
});
Expand All @@ -54,7 +54,8 @@ export class ResetDeviceController {
@Params() { id }: UUIDParam,
@Body() data: UpdateResetDeviceData
): Promise<void> {
const rpFlow = await getRepository(ResetPasswordFlow).findOne({
// TODO: Create ResetSecurityFlowService
const rpFlow = await getRepository(ResetSecurityFlow).findOne({
where: { id },
relations: ["contact"]
});
Expand All @@ -75,13 +76,10 @@ export class ResetDeviceController {
}

// Disable MFA
await ContactMfaService.delete(rpFlow.contact, rpFlow.contact.id, {
type: ContactMfaType.TOTP
});
await ContactMfaService.deleteUnsecure(rpFlow.contact);

// Stop reset flow
// TODO: ResetPasswordFlow -> ResetSecurityFlow
await getRepository(ResetPasswordFlow).delete(id);
await getRepository(ResetSecurityFlow).delete(id);

await login(req, rpFlow.contact);
}
Expand Down
10 changes: 6 additions & 4 deletions src/api/controllers/ResetPasswordController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { generatePassword } from "@core/utils/auth";
import ContactsService from "@core/services/ContactsService";
import EmailService from "@core/services/EmailService";

import ResetPasswordFlow from "@models/ResetPasswordFlow";
import ResetSecurityFlow from "@models/ResetSecurityFlow";

import { login } from "@api/utils";
import { UUIDParam } from "@api/data";
Expand All @@ -30,9 +30,10 @@ export class ResetPasswordController {
@OnUndefined(204)
@Post()
async create(@Body() data: CreateResetPasswordData): Promise<void> {
// TODO: Create ResetSecurityFlowService
const contact = await ContactsService.findOne({ email: data.email });
if (contact) {
const rpFlow = await getRepository(ResetPasswordFlow).save({ contact });
const rpFlow = await getRepository(ResetSecurityFlow).save({ contact });
await EmailService.sendTemplateToContact("reset-password", contact, {
rpLink: data.resetUrl + "/" + rpFlow.id
});
Expand All @@ -46,15 +47,16 @@ export class ResetPasswordController {
@Params() { id }: UUIDParam,
@Body() data: UpdateResetPasswordData
): Promise<void> {
const rpFlow = await getRepository(ResetPasswordFlow).findOne({
// TODO: Create ResetSecurityFlowService
const rpFlow = await getRepository(ResetSecurityFlow).findOne({
where: { id },
relations: ["contact"]
});
if (rpFlow) {
await ContactsService.updateContact(rpFlow.contact, {
password: await generatePassword(data.password)
});
await getRepository(ResetPasswordFlow).delete(id);
await getRepository(ResetSecurityFlow).delete(id);

await login(req, rpFlow.contact);
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/api/controllers/SegmentController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { getRepository } from "typeorm";

import { UUIDParam } from "@api/data";
import {
GetContactData,
GetContactsQuery,
fetchPaginatedContacts
} from "@api/data/ContactData";
Expand All @@ -34,6 +33,8 @@ import Segment from "@models/Segment";
import SegmentContact from "@models/SegmentContact";
import SegmentOngoingEmail from "@models/SegmentOngoingEmail";

import type { GetContactData } from "@type/get-contact-data";

@JsonController("/segments")
@Authorized("admin")
export class SegmentController {
Expand Down
2 changes: 1 addition & 1 deletion src/api/data/ApiKeyData/interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IsDate, IsIn, IsOptional, IsString } from "class-validator";
import { GetContactData } from "../ContactData";
import { GetContactData } from "@type/get-contact-data";
import { GetPaginatedQuery } from "../PaginatedData";
import { Type } from "class-transformer";

Expand Down
2 changes: 1 addition & 1 deletion src/api/data/CalloutResponseCommentData/interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Contact from "@models/Contact";
import { IsIn, IsObject, IsString } from "class-validator";
import { GetContactData } from "../ContactData";
import { GetContactData } from "@type/get-contact-data";
import { GetPaginatedQuery } from "../PaginatedData";

export interface UpdateCalloutResponseComment {
Expand Down
2 changes: 1 addition & 1 deletion src/api/data/CalloutResponseData/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { UUIDParam } from "..";
import { GetCalloutData } from "../CalloutData";
import { GetCalloutResponseCommentData } from "../CalloutResponseCommentData/interface";
import { GetCalloutTagData } from "../CalloutTagData";
import { GetContactData } from "../ContactData";
import { GetContactData } from "@type/get-contact-data";
import { GetPaginatedQuery, GetPaginatedRuleGroup } from "../PaginatedData";

export enum GetCalloutResponseWith {
Expand Down
11 changes: 5 additions & 6 deletions src/api/data/ContactData/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ import {
GetPaginatedRuleGroup
} from "@api/data/PaginatedData";

import {
GetContactData,
GetContactRoleData,
GetContactsQuery,
GetContactWith
} from "./interface";
import { GetContactWith } from "@enums/get-contact-with";

import type { GetContactData } from "@type/get-contact-data";
import type { GetContactRoleData } from "@type/get-contact-role-data";
import type { GetContactsQuery } from "@api/data/ContactData";

interface ConvertOpts {
with: GetContactWith[] | undefined;
Expand Down
Loading

0 comments on commit 0d1128a

Please sign in to comment.