Skip to content

Commit

Permalink
Merge pull request #6 from fga-eps-mds/feature/#136-autenticacao
Browse files Browse the repository at this point in the history
Feature/#136 autenticacao
  • Loading branch information
EduardoGurgel authored Oct 23, 2023
2 parents 35c1750 + 5ee1e6f commit c6cd3f9
Show file tree
Hide file tree
Showing 25 changed files with 628 additions and 12 deletions.
4 changes: 3 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ DB_PASS=postgres
DB_DATABASE=gerocuidado-usuario-db
DB_PORT=5001

#BCRYPT
#JWT TOKEN
JWT_TOKEN_SECRET=f57d8cc37a35a8051aa97b5ec8506a2ac479e81f82aed9de975a0cb90b903044
JWT_TOKEN_EXPIRES_IN=12h
HASH_SALT=10

4 changes: 3 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ DB_PASS=postgres
DB_DATABASE=gerocuidado-usuario-db-test
DB_PORT=5001

#BCRYPT
#JWT TOKEN
JWT_TOKEN_SECRET=f57d8cc37a35a8051aa97b5ec8506a2ac479e81f82aed9de975a0cb90b903044
JWT_TOKEN_EXPIRES_IN=12h
HASH_SALT=10
1 change: 1 addition & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ services:
user: root
ports:
- '3001:3001'
- '8001:8001'
depends_on:
- gerocuidado-usuario-db-test
networks:
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ services:
- NODE_ENV=development
ports:
- '3001:3001'
- '4001:4001'
- '7001:9001'
depends_on:
- gerocuidado-usuario-db
networks:
- gerocuidado-usuario-net
- gerocuidado-apis-net

gerocuidado-usuario-db:
build:
Expand All @@ -36,3 +38,6 @@ services:
networks:
gerocuidado-usuario-net:
driver: bridge
gerocuidado-apis-net:
name: gerocuidado-apis-net
external: true
106 changes: 104 additions & 2 deletions e2e/usuario.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { ClientProxy, ClientsModule, Transport } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { lastValueFrom, timeout } from 'rxjs';
import request from 'supertest';
import { Repository } from 'typeorm';
import { AppModule } from '../src/app.module';
Expand All @@ -11,19 +13,35 @@ import { Usuario } from '../src/usuario/entities/usuario.entity';

describe('E2E - Usuario', () => {
let app: INestApplication;
let client: ClientProxy;
let repository: Repository<Usuario>;
let token: string;

const senha = '123';

const user: Partial<Usuario> = {
id: undefined,
nome: 'Henrique',
email: '[email protected]',
senha: '123',
senha,
admin: false,
};

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
imports: [
AppModule,
ClientsModule.register([
{
name: 'AUTH_CLIENT_TEST',
transport: Transport.TCP,
options: {
host: '0.0.0.0',
port: 8001,
},
},
]),
],
}).compile();

app = moduleFixture.createNestApplication();
Expand All @@ -40,9 +58,20 @@ describe('E2E - Usuario', () => {
new ModelNotFoundExceptionFilter(),
);

app.connectMicroservice({
transport: Transport.TCP,
options: {
host: '0.0.0.0',
port: 8001,
},
});

await app.startAllMicroservices();
await app.init();

client = app.get('AUTH_CLIENT_TEST');
await client.connect();

repository = app.get<Repository<Usuario>>(getRepositoryToken(Usuario));
});

Expand Down Expand Up @@ -101,11 +130,78 @@ describe('E2E - Usuario', () => {
});
});

describe('POST - /api/usuario/login', () => {
it('should not successfully login - wrong password', async () => {
const res = await request(app.getHttpServer())
.post('/login')
.set('Content-Type', 'application/json')
.send({ email: user.email, senha: '1234' });

expect(res.statusCode).toEqual(401);
expect(res.body.message).toBe('Senha incorreta!');
expect(res.body.data).toBeNull();
});

it('should not successfully login - email not found', async () => {
const res = await request(app.getHttpServer())
.post('/login')
.set('Content-Type', 'application/json')
.send({ email: '[email protected]', senha });

expect(res.statusCode).toEqual(400);
expect(res.body.message).toBe('Este email não está cadastrado!');
expect(res.body.data).toBeNull();
});

it('should not get when not authenticated', async () => {
const res = await request(app.getHttpServer())
.get('')
.set('Content-Type', 'application/json')
.send();

expect(res.statusCode).toEqual(401);
expect(res.body.message).toBe('Usuário não autenticado!');
expect(res.body.data).toBeNull();
});

it('should successfully login', async () => {
const res = await request(app.getHttpServer())
.post('/login')
.set('Content-Type', 'application/json')
.send({ email: user.email, senha });

expect(res.statusCode).toEqual(200);
expect(res.body.message).toBe('Login efetuado com sucesso!');
expect(res.body.data).toBeDefined();

token = res.body.data;
});

it('should validate token', async () => {
const request = client
.send({ role: 'auth', cmd: 'check' }, { jwt: token })
.pipe(timeout(5000));

const response = await lastValueFrom(request);
expect(response.email).toBe(user.email);
});

it('should not validate token', async () => {
const request = client
.send({ role: 'auth', cmd: 'check' }, { jwt: 'invalid token' })
.pipe(timeout(5000));

const response = await lastValueFrom(request);
expect(response).toBe(false);
});
});

describe('GET - /api/usuario/:id', () => {
it('should successfully get "usuario" by id', async () => {
const res = await request(app.getHttpServer())
.get(`/${user.id}`)
.set('Content-Type', 'application/json')
.set('Authorization', 'bearer ' + token)
.send();

expect(res.statusCode).toEqual(200);
Expand All @@ -118,6 +214,7 @@ describe('E2E - Usuario', () => {
const res = await request(app.getHttpServer())
.get(`/${wrongId}`)
.set('Content-Type', 'application/json')
.set('Authorization', 'bearer ' + token)
.send();

expect(res.statusCode).toEqual(400);
Expand All @@ -130,6 +227,7 @@ describe('E2E - Usuario', () => {
const res = await request(app.getHttpServer())
.get('/9999')
.set('Content-Type', 'application/json')
.set('Authorization', 'bearer ' + token)
.send();

expect(res.statusCode).toEqual(404);
Expand All @@ -149,6 +247,7 @@ describe('E2E - Usuario', () => {
const res = await request(app.getHttpServer())
.get('?filter=' + JSON.stringify(filter))
.set('Content-Type', 'application/json')
.set('Authorization', 'bearer ' + token)
.send();

expect(res.statusCode).toEqual(200);
Expand All @@ -164,6 +263,7 @@ describe('E2E - Usuario', () => {
const res = await request(app.getHttpServer())
.patch(`/${user.id}`)
.set('Content-Type', 'application/json')
.set('Authorization', 'bearer ' + token)
.send(update);

user.nome = update.nome;
Expand All @@ -179,6 +279,7 @@ describe('E2E - Usuario', () => {
const res = await request(app.getHttpServer())
.delete(`/${user.id}`)
.set('Content-Type', 'application/json')
.set('Authorization', 'bearer ' + token)
.send();

delete user.id;
Expand All @@ -193,5 +294,6 @@ describe('E2E - Usuario', () => {
await repository.query('TRUNCATE usuario CASCADE');
await repository.delete({});
await app.close();
await client.close();
});
});
2 changes: 2 additions & 0 deletions src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { PublicRoute } from './shared/decorators/public-route.decorator';

@Controller()
export class AppController {
constructor(private readonly service: AppService) {}

@Get('health-check')
@PublicRoute()
healthCheck() {
return this.service.healthCheck();
}
Expand Down
12 changes: 11 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_GUARD } from '@nestjs/core';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AutenticacaoModule } from './autenticacao/autenticacao.module';
import { JwtAuthGuard } from './autenticacao/jwt-auth.guard';
import { DbModule } from './config/db/db.module';
import { DbService } from './config/db/db.service';
import { UsuarioModule } from './usuario/usuario.module';
Expand All @@ -21,8 +24,15 @@ const ENV = process.env.NODE_ENV;
}),
DbModule,
UsuarioModule,
AutenticacaoModule,
],
controllers: [AppController],
providers: [AppService],
providers: [
AppService,
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
],
})
export class AppModule {}
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 {}
Loading

0 comments on commit c6cd3f9

Please sign in to comment.