From 8ea01956105cc5e3825fa134c266478b5454d02b Mon Sep 17 00:00:00 2001 From: Nuno Caseiro Date: Mon, 21 Mar 2022 17:03:09 +0000 Subject: [PATCH 1/2] feat: check user exist methods --- backend/src/libs/dto/param/email.param.ts | 7 +++++++ .../src/libs/test-utils/mocks/user.mock.ts | 3 ++- .../auth/controller/auth.controller.ts | 20 +++++++++++++++++- backend/src/modules/auth/shared/login.auth.ts | 3 ++- .../azure/controller/azure.controller.ts | 21 ++++++++++++++----- backend/src/modules/azure/interfaces/types.ts | 2 +- .../azure/services/auth.azure.service.ts | 3 ++- .../applications/get.user.application.ts | 16 ++++++++++++++ .../users/dto/create.user.azure.dto.ts | 6 +++++- .../src/modules/users/dto/create.user.dto.ts | 8 ++++++- .../src/modules/users/dto/logged.user.dto.ts | 6 +++++- backend/src/modules/users/dto/user.dto.ts | 6 +++++- .../get.user.application.interface.ts | 6 ++++++ backend/src/modules/users/interfaces/types.ts | 3 +++ .../src/modules/users/schemas/user.schema.ts | 5 ++++- backend/src/modules/users/users.module.ts | 15 +++++++++++-- backend/src/modules/users/users.providers.ts | 5 +++++ backend/test/auth/auth.controller.spec.ts | 16 ++++++++++---- 18 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 backend/src/libs/dto/param/email.param.ts create mode 100644 backend/src/modules/users/applications/get.user.application.ts create mode 100644 backend/src/modules/users/interfaces/applications/get.user.application.interface.ts diff --git a/backend/src/libs/dto/param/email.param.ts b/backend/src/libs/dto/param/email.param.ts new file mode 100644 index 000000000..a6410b21f --- /dev/null +++ b/backend/src/libs/dto/param/email.param.ts @@ -0,0 +1,7 @@ +import { IsEmail, IsString } from 'class-validator'; + +export class EmailParam { + @IsEmail() + @IsString() + email!: string; +} diff --git a/backend/src/libs/test-utils/mocks/user.mock.ts b/backend/src/libs/test-utils/mocks/user.mock.ts index 8c47d7664..7ce6dab06 100644 --- a/backend/src/libs/test-utils/mocks/user.mock.ts +++ b/backend/src/libs/test-utils/mocks/user.mock.ts @@ -1,7 +1,8 @@ const mockedUser = { _id: '1', email: 'user1@email.com', - name: 'John', + firstName: 'John', + lastName: 'Doe', password: 'hash', }; diff --git a/backend/src/modules/auth/controller/auth.controller.ts b/backend/src/modules/auth/controller/auth.controller.ts index 6d7c5b7c3..c189dad62 100644 --- a/backend/src/modules/auth/controller/auth.controller.ts +++ b/backend/src/modules/auth/controller/auth.controller.ts @@ -9,6 +9,7 @@ import { Inject, NotFoundException, BadRequestException, + Param, } from '@nestjs/common'; import LocalAuthGuard from '../../../libs/guards/localAuth.guard'; import RequestWithUser from '../../../libs/interfaces/requestWithUser.interface'; @@ -23,6 +24,9 @@ import { import CreateUserDto from '../../users/dto/create.user.dto'; import { uniqueViolation } from '../../../infrastructure/database/errors/unique.user'; import { signIn } from '../shared/login.auth'; +import * as User from '../../users/interfaces/types'; +import { EmailParam } from '../../../libs/dto/param/email.param'; +import { GetUserApplication } from '../../users/interfaces/applications/get.user.application.interface'; @Controller('auth') export default class AuthController { @@ -31,13 +35,20 @@ export default class AuthController { private registerAuthApp: RegisterAuthApplication, @Inject(TYPES.applications.GetTokenAuthApplication) private getTokenAuthApp: GetTokenAuthApplication, + @Inject(User.TYPES.applications.GetUserApplication) + private getUserApp: GetUserApplication, ) {} @Post('register') async register(@Body() registrationData: CreateUserDto) { try { const user = await this.registerAuthApp.register(registrationData); - return { _id: user._id, name: user.name, email: user.email }; + return { + _id: user._id, + firstName: user.firstName, + lastName: user.lastName, + email: user.email, + }; } catch (error) { if (error.code === uniqueViolation) { throw new BadRequestException(EMAIL_EXISTS); @@ -65,4 +76,11 @@ export default class AuthController { } = request; return this.getTokenAuthApp.getAccessToken(id); } + + @Get('checkUserEmail/:email') + async checkEmail(@Param() emailParam: EmailParam) { + const { email } = emailParam; + const found = await this.getUserApp.getByEmail(email); + return !!found; + } } diff --git a/backend/src/modules/auth/shared/login.auth.ts b/backend/src/modules/auth/shared/login.auth.ts index 854744504..2f384573d 100644 --- a/backend/src/modules/auth/shared/login.auth.ts +++ b/backend/src/modules/auth/shared/login.auth.ts @@ -14,7 +14,8 @@ export const signIn = async ( return { ...jwt, email: user.email, - name: user.name, + firstName: user.firstName, + lastName: user.lastName, strategy, id: user._id, }; diff --git a/backend/src/modules/azure/controller/azure.controller.ts b/backend/src/modules/azure/controller/azure.controller.ts index 71e81701c..ee60f2f3b 100644 --- a/backend/src/modules/azure/controller/azure.controller.ts +++ b/backend/src/modules/azure/controller/azure.controller.ts @@ -1,13 +1,18 @@ -import { Body, Controller, Get, Inject, Post, Req } from '@nestjs/common'; +import { Body, Controller, Get, Inject, Param, Post } from '@nestjs/common'; +import { EmailParam } from '../../../libs/dto/param/email.param'; import { AuthAzureApplication } from '../interfaces/applications/auth.azure.application.interface'; import { AzureToken } from '../interfaces/token.azure.dto'; import { TYPES } from '../interfaces/types'; +import * as User from '../../users/interfaces/types'; +import { GetUserApplication } from '../../users/interfaces/applications/get.user.application.interface'; @Controller('auth') export default class AzureController { constructor( @Inject(TYPES.applications.AuthAzureApplication) private authAzureApp: AuthAzureApplication, + @Inject(User.TYPES.applications.GetUserApplication) + private getUserApp: GetUserApplication, ) {} @Post('signAzure') @@ -15,9 +20,15 @@ export default class AzureController { return this.authAzureApp.registerOrLogin(azureToken.token); } - @Get('checkUserExists/:email') - checkUserExists(@Req() request) { - const { email } = request.params; - return this.authAzureApp.checkUserExistsInActiveDirectory(email); + @Get('checkUserEmailAD/:email') + async checkEmail(@Param() emailParam: EmailParam) { + const { email } = emailParam; + const existAzure = await this.authAzureApp.checkUserExistsInActiveDirectory( + email, + ); + if (existAzure) return 'az'; + const existDB = await this.getUserApp.getByEmail(email); + if (existDB) return 'local'; + return false; } } diff --git a/backend/src/modules/azure/interfaces/types.ts b/backend/src/modules/azure/interfaces/types.ts index 3b8d4a967..2bb5e5950 100644 --- a/backend/src/modules/azure/interfaces/types.ts +++ b/backend/src/modules/azure/interfaces/types.ts @@ -4,6 +4,6 @@ export const TYPES = { CronAzureService: 'CronAzureService', }, applications: { - AuthAzureApplication: 'AuthAuthApplication', + AuthAzureApplication: 'AuthAzureApplication', }, }; diff --git a/backend/src/modules/azure/services/auth.azure.service.ts b/backend/src/modules/azure/services/auth.azure.service.ts index 73d231af4..b4c39972e 100644 --- a/backend/src/modules/azure/services/auth.azure.service.ts +++ b/backend/src/modules/azure/services/auth.azure.service.ts @@ -47,7 +47,8 @@ export default class AuthAzureServiceImpl implements AuthAzureService { const createdUser = await this.createUserService.create({ email: email ?? unique_name, - name: `${given_name} ${family_name}`, + firstName: given_name, + lastName: family_name, }); if (createdUser) return signIn(createdUser, this.getTokenService, 'azure'); diff --git a/backend/src/modules/users/applications/get.user.application.ts b/backend/src/modules/users/applications/get.user.application.ts new file mode 100644 index 000000000..dfd978c4b --- /dev/null +++ b/backend/src/modules/users/applications/get.user.application.ts @@ -0,0 +1,16 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { GetUserApplication } from '../interfaces/applications/get.user.application.interface'; +import { GetUserService } from '../interfaces/services/get.user.service.interface'; +import { TYPES } from '../interfaces/types'; + +@Injectable() +export class GetUserApplicationImpl implements GetUserApplication { + constructor( + @Inject(TYPES.services.GetUserService) + private getUserService: GetUserService, + ) {} + + getByEmail(email: string) { + return this.getUserService.getByEmail(email); + } +} diff --git a/backend/src/modules/users/dto/create.user.azure.dto.ts b/backend/src/modules/users/dto/create.user.azure.dto.ts index 8baa73800..2d53dab32 100644 --- a/backend/src/modules/users/dto/create.user.azure.dto.ts +++ b/backend/src/modules/users/dto/create.user.azure.dto.ts @@ -3,7 +3,11 @@ import { IsNotEmpty, IsString } from 'class-validator'; export default class CreateUserAzureDto { @IsNotEmpty() @IsString() - name!: string; + firstName!: string; + + @IsNotEmpty() + @IsString() + lastName!: string; @IsNotEmpty() @IsString() diff --git a/backend/src/modules/users/dto/create.user.dto.ts b/backend/src/modules/users/dto/create.user.dto.ts index bbabee19c..0f7bf9c5e 100644 --- a/backend/src/modules/users/dto/create.user.dto.ts +++ b/backend/src/modules/users/dto/create.user.dto.ts @@ -17,7 +17,13 @@ export default class CreateUserDto { @IsNotEmpty() @MinLength(3) @Transform(({ value }: TransformFnParams) => value.trim()) - name!: string; + firstName!: string; + + @IsString() + @IsNotEmpty() + @MinLength(3) + @Transform(({ value }: TransformFnParams) => value.trim()) + lastName!: string; @IsString() @IsNotEmpty() diff --git a/backend/src/modules/users/dto/logged.user.dto.ts b/backend/src/modules/users/dto/logged.user.dto.ts index af9051e85..a449bb698 100644 --- a/backend/src/modules/users/dto/logged.user.dto.ts +++ b/backend/src/modules/users/dto/logged.user.dto.ts @@ -9,7 +9,11 @@ export default class LoggedUserDto { @IsNotEmpty() @IsString() - name!: string; + firstName!: string; + + @IsNotEmpty() + @IsString() + lastName!: string; @IsNotEmpty() @IsString() diff --git a/backend/src/modules/users/dto/user.dto.ts b/backend/src/modules/users/dto/user.dto.ts index 72d6ed666..78b5f9829 100644 --- a/backend/src/modules/users/dto/user.dto.ts +++ b/backend/src/modules/users/dto/user.dto.ts @@ -8,7 +8,11 @@ export default class UserDto { @IsNotEmpty() @IsString() - name!: string; + firstName!: string; + + @IsNotEmpty() + @IsString() + lastName!: string; @IsNotEmpty() @IsString() diff --git a/backend/src/modules/users/interfaces/applications/get.user.application.interface.ts b/backend/src/modules/users/interfaces/applications/get.user.application.interface.ts new file mode 100644 index 000000000..828d29bbe --- /dev/null +++ b/backend/src/modules/users/interfaces/applications/get.user.application.interface.ts @@ -0,0 +1,6 @@ +import { LeanDocument } from 'mongoose'; +import { UserDocument } from '../../schemas/user.schema'; + +export interface GetUserApplication { + getByEmail(email: string): Promise | null>; +} diff --git a/backend/src/modules/users/interfaces/types.ts b/backend/src/modules/users/interfaces/types.ts index 23d598eb3..5dc3dd6ec 100644 --- a/backend/src/modules/users/interfaces/types.ts +++ b/backend/src/modules/users/interfaces/types.ts @@ -4,4 +4,7 @@ export const TYPES = { GetUserService: 'GetUserService', UpdateUserService: 'UpdateUserService', }, + applications: { + GetUserApplication: 'GetUserApplication', + }, }; diff --git a/backend/src/modules/users/schemas/user.schema.ts b/backend/src/modules/users/schemas/user.schema.ts index 78b42c764..f5e4735ae 100644 --- a/backend/src/modules/users/schemas/user.schema.ts +++ b/backend/src/modules/users/schemas/user.schema.ts @@ -6,7 +6,10 @@ export type UserDocument = User & mongoose.Document; @Schema() export default class User { @Prop({ nullable: false }) - name!: string; + firstName!: string; + + @Prop({ nullable: false }) + lastName!: string; @Prop({ nullable: false }) password!: string; diff --git a/backend/src/modules/users/users.module.ts b/backend/src/modules/users/users.module.ts index f314b4c09..83a82c58c 100644 --- a/backend/src/modules/users/users.module.ts +++ b/backend/src/modules/users/users.module.ts @@ -4,11 +4,22 @@ import { createUserService, getUserService, updateUserService, + getUserApplication, } from './users.providers'; @Module({ imports: [mongooseUserModule], - providers: [createUserService, getUserService, updateUserService], - exports: [createUserService, getUserService, updateUserService], + providers: [ + createUserService, + getUserService, + updateUserService, + getUserApplication, + ], + exports: [ + createUserService, + getUserService, + updateUserService, + getUserApplication, + ], }) export default class UsersModule {} diff --git a/backend/src/modules/users/users.providers.ts b/backend/src/modules/users/users.providers.ts index e28ba4894..9f76c7f46 100644 --- a/backend/src/modules/users/users.providers.ts +++ b/backend/src/modules/users/users.providers.ts @@ -1,3 +1,4 @@ +import { GetUserApplicationImpl } from './applications/get.user.application'; import { TYPES } from './interfaces/types'; import CreateUserServiceImpl from './services/create.user.service'; import GetUserServiceImpl from './services/get.user.service'; @@ -15,3 +16,7 @@ export const updateUserService = { provide: TYPES.services.UpdateUserService, useClass: UpdateUserServiceImpl, }; +export const getUserApplication = { + provide: TYPES.applications.GetUserApplication, + useClass: GetUserApplicationImpl, +}; diff --git a/backend/test/auth/auth.controller.spec.ts b/backend/test/auth/auth.controller.spec.ts index 59ca34007..b69434e67 100644 --- a/backend/test/auth/auth.controller.spec.ts +++ b/backend/test/auth/auth.controller.spec.ts @@ -16,6 +16,8 @@ import { } from '../../src/modules/auth/auth.providers'; import { createUserService, + getUserApplication, + getUserService, updateUserService, } from '../../src/modules/users/users.providers'; @@ -37,6 +39,8 @@ describe('AuthController', () => { registerAuthService, updateUserService, createUserService, + getUserApplication, + getUserService, ConfigService, { provide: ConfigService, @@ -65,7 +69,8 @@ describe('AuthController', () => { .post('/auth/register') .send({ email: mockedUser.email, - name: mockedUser.name, + firstName: mockedUser.firstName, + lastName: mockedUser.lastName, password: '1!Aab2CD', }) .expect(201); @@ -81,14 +86,16 @@ describe('AuthController', () => { request(app.getHttpServer()) .post('/auth/register') .send({ - name: mockedUser.name, + firstName: mockedUser.firstName, + lastName: mockedUser.lastName, }) .expect(400)); it('should throw an error because full data wasnt submitted', async () => request(app.getHttpServer()) .post('/auth/register') .send({ - name: mockedUser.name, + firstName: mockedUser.firstName, + lastName: mockedUser.lastName, password: mockedUser.password, }) .expect(400)); @@ -96,7 +103,8 @@ describe('AuthController', () => { const res = await request(app.getHttpServer()) .post('/auth/register') .send({ - name: mockedUser.name, + firstName: mockedUser.firstName, + lastName: mockedUser.lastName, password: '1234', email: mockedUser.email, }); From 73cc9fe71a7eed803ecc7cb6c81688d4f54a230b Mon Sep 17 00:00:00 2001 From: Nuno Caseiro Date: Mon, 21 Mar 2022 20:49:06 +0000 Subject: [PATCH 2/2] refactor: pr review suggestions applied --- backend/package.json | 3 ++- backend/src/libs/test-utils/mocks/user.mock.ts | 12 +++++++----- .../modules/auth/controller/auth.controller.ts | 11 ++++++----- backend/src/modules/auth/shared/login.auth.ts | 11 ++++++----- .../modules/azure/controller/azure.controller.ts | 11 +++++------ package-lock.json | 16 ++++++++++++++++ 6 files changed, 42 insertions(+), 22 deletions(-) diff --git a/backend/package.json b/backend/package.json index 5f0982d8a..058823b00 100644 --- a/backend/package.json +++ b/backend/package.json @@ -56,7 +56,8 @@ "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.5.5", - "socket.io": "^4.4.1" + "socket.io": "^4.4.1", + "@faker-js/faker": "^6.0.0" }, "devDependencies": { "@nestjs/cli": "^8.2.3", diff --git a/backend/src/libs/test-utils/mocks/user.mock.ts b/backend/src/libs/test-utils/mocks/user.mock.ts index 7ce6dab06..7b5d1a769 100644 --- a/backend/src/libs/test-utils/mocks/user.mock.ts +++ b/backend/src/libs/test-utils/mocks/user.mock.ts @@ -1,9 +1,11 @@ +import { faker } from '@faker-js/faker'; + const mockedUser = { - _id: '1', - email: 'user1@email.com', - firstName: 'John', - lastName: 'Doe', - password: 'hash', + _id: faker.datatype.uuid(), + email: faker.internet.email(), + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + password: faker.internet.password(), }; export default mockedUser; diff --git a/backend/src/modules/auth/controller/auth.controller.ts b/backend/src/modules/auth/controller/auth.controller.ts index c189dad62..8fb560ab5 100644 --- a/backend/src/modules/auth/controller/auth.controller.ts +++ b/backend/src/modules/auth/controller/auth.controller.ts @@ -42,12 +42,13 @@ export default class AuthController { @Post('register') async register(@Body() registrationData: CreateUserDto) { try { - const user = await this.registerAuthApp.register(registrationData); + const { _id, firstName, lastName, email } = + await this.registerAuthApp.register(registrationData); return { - _id: user._id, - firstName: user.firstName, - lastName: user.lastName, - email: user.email, + _id, + firstName, + lastName, + email, }; } catch (error) { if (error.code === uniqueViolation) { diff --git a/backend/src/modules/auth/shared/login.auth.ts b/backend/src/modules/auth/shared/login.auth.ts index 2f384573d..ee9cf31ea 100644 --- a/backend/src/modules/auth/shared/login.auth.ts +++ b/backend/src/modules/auth/shared/login.auth.ts @@ -9,14 +9,15 @@ export const signIn = async ( getTokenService: GetTokenAuthService | GetTokenAuthApplication, strategy: string, ) => { - const jwt = await getTokenService.getTokens(user._id); + const { email, firstName, lastName, _id } = user; + const jwt = await getTokenService.getTokens(_id); if (!jwt) return null; return { ...jwt, - email: user.email, - firstName: user.firstName, - lastName: user.lastName, + email, + firstName, + lastName, strategy, - id: user._id, + id: _id, }; }; diff --git a/backend/src/modules/azure/controller/azure.controller.ts b/backend/src/modules/azure/controller/azure.controller.ts index ee60f2f3b..c3384efbc 100644 --- a/backend/src/modules/azure/controller/azure.controller.ts +++ b/backend/src/modules/azure/controller/azure.controller.ts @@ -23,12 +23,11 @@ export default class AzureController { @Get('checkUserEmailAD/:email') async checkEmail(@Param() emailParam: EmailParam) { const { email } = emailParam; - const existAzure = await this.authAzureApp.checkUserExistsInActiveDirectory( - email, - ); - if (existAzure) return 'az'; - const existDB = await this.getUserApp.getByEmail(email); - if (existDB) return 'local'; + const existUserInAzure = + await this.authAzureApp.checkUserExistsInActiveDirectory(email); + if (existUserInAzure) return 'az'; + const existUserInDB = await this.getUserApp.getByEmail(email); + if (existUserInDB) return 'local'; return false; } } diff --git a/package-lock.json b/package-lock.json index 69b6bab31..55891dbb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@faker-js/faker": "^6.0.0", "@nestjs/common": "^8.4.1", "@nestjs/config": "^2.0.0", "@nestjs/core": "^8.4.1", @@ -2452,6 +2453,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@faker-js/faker": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-6.0.0.tgz", + "integrity": "sha512-10zLCKhp3YEmBuko71ivcMoIZcCLXgQVck6aNswX+AWwaek/L8S3yz9i8m3tHigRkcF6F2vI+qtdtyySHK+bGA==", + "engines": { + "node": ">=14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -20235,6 +20245,11 @@ } } }, + "@faker-js/faker": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-6.0.0.tgz", + "integrity": "sha512-10zLCKhp3YEmBuko71ivcMoIZcCLXgQVck6aNswX+AWwaek/L8S3yz9i8m3tHigRkcF6F2vI+qtdtyySHK+bGA==" + }, "@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -22803,6 +22818,7 @@ "backend": { "version": "file:backend", "requires": { + "@faker-js/faker": "^6.0.0", "@nestjs/cli": "^8.2.3", "@nestjs/common": "^8.4.1", "@nestjs/config": "^2.0.0",