Skip to content

Commit

Permalink
(#136) adiciona autenticacao controller e service
Browse files Browse the repository at this point in the history
  • Loading branch information
HenriqueAmorim20 committed Oct 22, 2023
1 parent 5c25c4f commit 7a89bc2
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/autenticacao/autenticacao.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HttpResponse } from '../shared/classes/http-response';
import { AutenticacaoController } from './autenticacao.controller';
import { AutenticacaoService } from './autenticacao.service';

describe('AutenticacaoController', () => {
let controller: AutenticacaoController;
let service: AutenticacaoService;

const mockAutenticacaoService = {
login: jest.fn(),
validateToken: jest.fn(),
};

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
controllers: [AutenticacaoController],
providers: [
{
provide: AutenticacaoService,
useValue: mockAutenticacaoService,
},
],
}).compile();

controller = module.get<AutenticacaoController>(AutenticacaoController);
service = module.get<AutenticacaoService>(AutenticacaoService);
});

it('should be defined', () => {
expect(controller).toBeDefined();
expect(service).toBeDefined();
});

it('should login', async () => {
jest.spyOn(service, 'login').mockReturnValue(Promise.resolve('token'));

expect(await controller.login({ email: 'a', senha: 'a' })).toEqual(
new HttpResponse('token').onLogin(),
);
});

it('should validateToken', async () => {
jest.spyOn(service, 'validateToken').mockReturnValue(true);

expect(await controller.validateToken({ jwt: 'token' })).toEqual(true);
});

it('should not validateToken', async () => {
jest.spyOn(service, 'validateToken').mockImplementation(() => {
throw new Error();
});

expect(await controller.validateToken({ jwt: 'token' })).toEqual(false);
});
});
29 changes: 29 additions & 0 deletions src/autenticacao/autenticacao.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { HttpResponse } from '../shared/classes/http-response';
import { PublicRoute } from '../shared/decorators/public-route.decorator';
import { Response } from '../shared/interceptors/data-transform.interceptor';
import { AutenticacaoService } from './autenticacao.service';
import { LoginDto } from './dto/login.dto';

@Controller()
export class AutenticacaoController {
constructor(private readonly _service: AutenticacaoService) {}

@Post('login')
@PublicRoute()
@HttpCode(200)
async login(@Body() body: LoginDto): Promise<Response<string>> {
const jwtToken = await this._service.login(body);
return new HttpResponse<string>(jwtToken).onLogin();
}

@MessagePattern({ role: 'auth', cmd: 'check' })
async validateToken(data: { jwt: string }) {
try {
return this._service.validateToken(data.jwt);
} catch (error) {
return false;
}
}
}
26 changes: 26 additions & 0 deletions src/autenticacao/autenticacao.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { UsuarioModule } from '../usuario/usuario.module';
import { AutenticacaoController } from './autenticacao.controller';
import { AutenticacaoService } from './autenticacao.service';
import { JwtStrategy } from './jwt.strategy';

@Module({
imports: [
UsuarioModule,
PassportModule,
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
secret: configService.get<string>('JWT_TOKEN_SECRET'),
}),
inject: [ConfigService],
}),
],
controllers: [AutenticacaoController],
providers: [AutenticacaoService, JwtStrategy],
exports: [AutenticacaoService],
})
export class AutenticacaoModule {}
87 changes: 87 additions & 0 deletions src/autenticacao/autenticacao.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Test, TestingModule } from '@nestjs/testing';
import bcrypt from 'bcrypt';
import { UsuarioService } from '../usuario/usuario.service';
import { AutenticacaoService } from './autenticacao.service';

describe('AutenticacaoService', () => {
let service: AutenticacaoService;
let usuarioService: UsuarioService;
let configService: ConfigService;
let jwtService: JwtService;

const mockUsuarioService = {
findByEmail: jest.fn(),
};

const mockConfigService = {
get: jest.fn(),
};

const mockJwtService = {
sign: jest.fn(),
verify: jest.fn(),
};

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AutenticacaoService,
{ provide: UsuarioService, useValue: mockUsuarioService },
{ provide: ConfigService, useValue: mockConfigService },
{ provide: JwtService, useValue: mockJwtService },
],
}).compile();

service = module.get<AutenticacaoService>(AutenticacaoService);
usuarioService = module.get<UsuarioService>(UsuarioService);
configService = module.get<ConfigService>(ConfigService);
jwtService = module.get<JwtService>(JwtService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});

it('should not login - no email found', async () => {
jest.spyOn(usuarioService, 'findByEmail').mockReturnValue(undefined as any);

expect(service.login({ email: 'a', senha: '1' })).rejects.toThrow(
new BadRequestException('Este email não está cadastrado!'),
);
});

it('should not login - wrong password', async () => {
jest.spyOn(usuarioService, 'findByEmail').mockReturnValue('a' as any);
jest
.spyOn(bcrypt, 'compare')
.mockImplementation((pass: string | Buffer, hash: string) =>
Promise.resolve(false),
);

expect(service.login({ email: 'a', senha: '1' })).rejects.toThrow(
new UnauthorizedException('Senha incorreta!'),
);
});

it('should login', async () => {
jest.spyOn(configService, 'get').mockReturnValue('12h');
jest.spyOn(jwtService, 'sign').mockReturnValue('token');
jest.spyOn(usuarioService, 'findByEmail').mockReturnValue('a' as any);
jest
.spyOn(bcrypt, 'compare')
.mockImplementation((pass: string | Buffer, hash: string) =>
Promise.resolve(true),
);

expect(await service.login({ email: 'a', senha: '1' })).toEqual('token');
});

it('should validate token', () => {
jest.spyOn(jwtService, 'verify').mockReturnValue(true as any);

expect(service.validateToken('token')).toEqual(true);
});
});
53 changes: 53 additions & 0 deletions src/autenticacao/autenticacao.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import bcrypt from 'bcrypt';
import { UsuarioService } from '../usuario/usuario.service';
import { LoginDto } from './dto/login.dto';

@Injectable()
export class AutenticacaoService {
constructor(
private readonly _usuarioService: UsuarioService,
private readonly _jwtService: JwtService,
private readonly _configService: ConfigService,
) {}

async login({ email, senha }: LoginDto): Promise<string> {
const userFound = await this._usuarioService.findByEmail(email);

if (!userFound) {
throw new BadRequestException('Este email não está cadastrado!');
}

await this.verifyPassword(senha, userFound.senha);

const JwtPayload = {
id: userFound.id,
email: userFound.email,
nome: userFound.nome,
admin: userFound.admin,
};

const expiresIn = this._configService.get('JWT_TOKEN_EXPIRES_IN');

return this._jwtService.sign(JwtPayload, { expiresIn });
}

private async verifyPassword(senha: string, hash: string) {
// TODO a senha deve vir do front como um base64
const isMatch = await bcrypt.compare(senha, hash);

if (!isMatch) {
throw new UnauthorizedException('Senha incorreta!');
}
}

validateToken(jwt: string) {
return this._jwtService.verify(jwt);
}
}
12 changes: 12 additions & 0 deletions src/autenticacao/dto/login.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';

export class LoginDto {
@IsString()
@IsNotEmpty()
@IsEmail()
email!: string;

@IsString()
@IsNotEmpty()
senha!: string;
}

0 comments on commit 7a89bc2

Please sign in to comment.