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

Commit

Permalink
Merge pull request #322 from beabee-communityrm/feat/mfa-reset
Browse files Browse the repository at this point in the history
feat!: Reset MFA via email
  • Loading branch information
wpf500 authored Nov 24, 2023
2 parents e1e3859 + 275a878 commit 3c92d4d
Show file tree
Hide file tree
Showing 58 changed files with 792 additions and 296 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ BEABEE_CURRENCYCODE=GBP
BEABEE_CURRENCYSYMBOL=£

TYPEORM_URL=postgres://membership_system:membership_system@db/membership_system
TYPEORM_SYNCHRONIZE=false
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
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"
}
}
6 changes: 3 additions & 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 All @@ -29,6 +27,7 @@ import { SegmentController } from "./controllers/SegmentController";
import { SignupController } from "./controllers/SignupController";
import { StatsController } from "./controllers/StatsController";
import { ResetPasswordController } from "./controllers/ResetPasswordController";
import { ResetDeviceController } from "./controllers/ResetDeviceController";
import { UploadController } from "./controllers/UploadController";

import * as db from "@core/database";
Expand Down Expand Up @@ -86,6 +85,7 @@ db.connect().then(() => {
SignupController,
StatsController,
ResetPasswordController,
ResetDeviceController,
UploadController
],
currentUserChecker,
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
7 changes: 3 additions & 4 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
46 changes: 46 additions & 0 deletions src/api/controllers/ResetDeviceController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Request } from "express";
import {
Body,
JsonController,
OnUndefined,
Params,
Post,
Put,
Req
} from "routing-controllers";

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

import { login } from "@api/utils";
import { UUIDParam } from "@api/data";
import {
CreateResetDeviceData,
UpdateResetDeviceData
} from "@api/data/ResetDeviceData";

@JsonController("/reset-device")
export class ResetDeviceController {
@OnUndefined(204)
@Post()
async create(@Body() data: CreateResetDeviceData): Promise<void> {
await ContactsService.resetDeviceBegin(
data.email,
data.type,
data.resetUrl
);
}

@OnUndefined(204)
@Put("/:id")
async complete(
@Req() req: Request,
@Params() { id }: UUIDParam,
@Body() data: UpdateResetDeviceData
): Promise<void> {
const contact = await ContactsService.resetDeviceComplete(
id,
data.password
);
await login(req, contact);
}
}
31 changes: 3 additions & 28 deletions src/api/controllers/ResetPasswordController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,14 @@ import { Request } from "express";
import {
Body,
JsonController,
NotFoundError,
OnUndefined,
Params,
Post,
Put,
Req
} from "routing-controllers";
import { getRepository } from "typeorm";

import { generatePassword } from "@core/utils/auth";

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

import ResetPasswordFlow from "@models/ResetPasswordFlow";

import { login } from "@api/utils";
import { UUIDParam } from "@api/data";
Expand All @@ -30,13 +23,7 @@ export class ResetPasswordController {
@OnUndefined(204)
@Post()
async create(@Body() data: CreateResetPasswordData): Promise<void> {
const contact = await ContactsService.findOne({ email: data.email });
if (contact) {
const rpFlow = await getRepository(ResetPasswordFlow).save({ contact });
await EmailService.sendTemplateToContact("reset-password", contact, {
rpLink: data.resetUrl + "/" + rpFlow.id
});
}
await ContactsService.resetPasswordBegin(data.email, data.resetUrl);
}

@OnUndefined(204)
Expand All @@ -46,19 +33,7 @@ export class ResetPasswordController {
@Params() { id }: UUIDParam,
@Body() data: UpdateResetPasswordData
): Promise<void> {
const rpFlow = await getRepository(ResetPasswordFlow).findOne({
where: { id },
relations: ["contact"]
});
if (rpFlow) {
await ContactsService.updateContact(rpFlow.contact, {
password: await generatePassword(data.password)
});
await getRepository(ResetPasswordFlow).delete(id);

await login(req, rpFlow.contact);
} else {
throw new NotFoundError();
}
const contact = await ContactsService.resetPasswordComplete(id, data);
await login(req, contact);
}
}
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 3c92d4d

Please sign in to comment.