add opa, refactor auth to authentication

This commit is contained in:
Gsk54
2023-01-16 15:03:58 +01:00
parent 0a2a44bc15
commit 6802cd3620
61 changed files with 456 additions and 381 deletions

View File

@@ -0,0 +1,56 @@
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
import { Controller } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { UpdateUsernameCommand } from '../../commands/update-username.command';
import { Type } from '../../domain/dtos/type.enum';
import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request';
import { DeleteAuthenticationRequest } from '../../domain/dtos/delete-authentication.request';
import { DeleteAuthenticationCommand } from '../../commands/delete-authentication.command';
@Controller()
export class AuthenticationMessagerController {
constructor(private readonly _commandBus: CommandBus) {}
@RabbitSubscribe({
exchange: 'user',
routingKey: 'update',
queue: 'auth-user-update',
})
public async userUpdatedHandler(message: string) {
const updatedUser = JSON.parse(message);
if (!updatedUser.hasOwnProperty('uuid')) throw new Error();
if (updatedUser.hasOwnProperty('email') && updatedUser.email) {
const updateUsernameRequest = new UpdateUsernameRequest();
updateUsernameRequest.uuid = updatedUser.uuid;
updateUsernameRequest.username = updatedUser.email;
updateUsernameRequest.type = Type.EMAIL;
await this._commandBus.execute(
new UpdateUsernameCommand(updateUsernameRequest),
);
}
if (updatedUser.hasOwnProperty('phone') && updatedUser.phone) {
const updateUsernameRequest = new UpdateUsernameRequest();
updateUsernameRequest.uuid = updatedUser.uuid;
updateUsernameRequest.username = updatedUser.phone;
updateUsernameRequest.type = Type.PHONE;
await this._commandBus.execute(
new UpdateUsernameCommand(updateUsernameRequest),
);
}
}
@RabbitSubscribe({
exchange: 'user',
routingKey: 'delete',
queue: 'auth-user-delete',
})
public async userDeletedHandler(message: string) {
const deletedUser = JSON.parse(message);
if (!deletedUser.hasOwnProperty('uuid')) throw new Error();
const deleteAuthRequest = new DeleteAuthenticationRequest();
deleteAuthRequest.uuid = deletedUser.uuid;
await this._commandBus.execute(
new DeleteAuthenticationCommand(deleteAuthRequest),
);
}
}

View File

@@ -0,0 +1,174 @@
import { Mapper } from '@automapper/core';
import { InjectMapper } from '@automapper/nestjs';
import { Controller, UsePipes } from '@nestjs/common';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { DatabaseException } from 'src/modules/database/src/exceptions/DatabaseException';
import { AddUsernameCommand } from '../../commands/add-username.command';
import { CreateAuthenticationCommand } from '../../commands/create-authentication.command';
import { DeleteAuthenticationCommand } from '../../commands/delete-authentication.command';
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
import { UpdatePasswordCommand } from '../../commands/update-password.command';
import { UpdateUsernameCommand } from '../../commands/update-username.command';
import { AddUsernameRequest } from '../../domain/dtos/add-username.request';
import { CreateAuthenticationRequest } from '../../domain/dtos/create-authentication.request';
import { DeleteAuthenticationRequest } from '../../domain/dtos/delete-authentication.request';
import { DeleteUsernameRequest } from '../../domain/dtos/delete-username.request';
import { UpdatePasswordRequest } from '../../domain/dtos/update-password.request';
import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request';
import { ValidateAuthRequest } from '../../domain/dtos/validate-authentication.request';
import { Authentication } from '../../domain/entities/authentication';
import { Username } from '../../domain/entities/username';
import { ValidateAuthenticationQuery } from '../../queries/validate-authentication.query';
import { AuthenticationPresenter } from './authentication.presenter';
import { RpcValidationPipe } from './rpc.validation-pipe';
import { UsernamePresenter } from './username.presenter';
@UsePipes(
new RpcValidationPipe({
whitelist: true,
forbidUnknownValues: false,
}),
)
@Controller()
export class AuthenticationController {
constructor(
private readonly _commandBus: CommandBus,
private readonly _queryBus: QueryBus,
@InjectMapper() private readonly _mapper: Mapper,
) {}
@GrpcMethod('AuthService', 'Validate')
async validate(data: ValidateAuthRequest): Promise<AuthenticationPresenter> {
try {
const auth: Authentication = await this._queryBus.execute(
new ValidateAuthenticationQuery(data.username, data.password),
);
return this._mapper.map(auth, Authentication, AuthenticationPresenter);
} catch (e) {
throw new RpcException({
code: 7,
message: 'Permission denied',
});
}
}
@GrpcMethod('AuthService', 'Create')
async createUser(
data: CreateAuthenticationRequest,
): Promise<AuthenticationPresenter> {
try {
const auth: Authentication = await this._commandBus.execute(
new CreateAuthenticationCommand(data),
);
return this._mapper.map(auth, Authentication, AuthenticationPresenter);
} catch (e) {
if (e instanceof DatabaseException) {
if (e.message.includes('Already exists')) {
throw new RpcException({
code: 6,
message: 'Auth already exists',
});
}
}
throw new RpcException({
code: 7,
message: 'Permission denied',
});
}
}
@GrpcMethod('AuthService', 'AddUsername')
async addUsername(data: AddUsernameRequest): Promise<UsernamePresenter> {
try {
const username: Username = await this._commandBus.execute(
new AddUsernameCommand(data),
);
return this._mapper.map(username, Username, UsernamePresenter);
} catch (e) {
if (e instanceof DatabaseException) {
if (e.message.includes('Already exists')) {
throw new RpcException({
code: 6,
message: 'Username already exists',
});
}
}
throw new RpcException({
code: 7,
message: 'Permission denied',
});
}
}
@GrpcMethod('AuthService', 'UpdateUsername')
async updateUsername(
data: UpdateUsernameRequest,
): Promise<UsernamePresenter> {
try {
const username: Username = await this._commandBus.execute(
new UpdateUsernameCommand(data),
);
return this._mapper.map(username, Username, UsernamePresenter);
} catch (e) {
if (e instanceof DatabaseException) {
if (e.message.includes('Already exists')) {
throw new RpcException({
code: 6,
message: 'Username already exists',
});
}
}
throw new RpcException({
code: 7,
message: 'Permission denied',
});
}
}
@GrpcMethod('AuthService', 'UpdatePassword')
async updatePassword(
data: UpdatePasswordRequest,
): Promise<AuthenticationPresenter> {
try {
const auth: Authentication = await this._commandBus.execute(
new UpdatePasswordCommand(data),
);
return this._mapper.map(auth, Authentication, AuthenticationPresenter);
} catch (e) {
throw new RpcException({
code: 7,
message: 'Permission denied',
});
}
}
@GrpcMethod('AuthService', 'DeleteUsername')
async deleteUsername(data: DeleteUsernameRequest) {
try {
return await this._commandBus.execute(new DeleteUsernameCommand(data));
} catch (e) {
throw new RpcException({
code: 7,
message: 'Permission denied',
});
}
}
@GrpcMethod('AuthService', 'Delete')
async deleteAuth(data: DeleteAuthenticationRequest) {
try {
return await this._commandBus.execute(
new DeleteAuthenticationCommand(data),
);
} catch (e) {
throw new RpcException({
code: 7,
message: 'Permission denied',
});
}
}
}

View File

@@ -0,0 +1,6 @@
import { AutoMap } from '@automapper/classes';
export class AuthenticationPresenter {
@AutoMap()
uuid: string;
}

View File

@@ -0,0 +1,47 @@
syntax = "proto3";
package authentication;
service AuthenticationService {
rpc Validate(AuthenticationByUsernamePassword) returns (Uuid);
rpc Create(Authentication) returns (Uuid);
rpc AddUsername(Username) returns (Uuid);
rpc UpdatePassword(Password) returns (Uuid);
rpc UpdateUsername(Username) returns (Uuid);
rpc DeleteUsername(Username) returns (Uuid);
rpc Delete(Uuid) returns (Empty);
}
message AuthenticationByUsernamePassword {
string username = 1;
string password = 2;
}
enum Type {
EMAIL = 0;
PHONE = 1;
}
message Authentication {
string uuid = 1;
string username = 2;
string password = 3;
Type type = 4;
}
message Password {
string uuid = 1;
string password = 2;
}
message Username {
string uuid = 1;
string username = 2;
Type type = 3;
}
message Uuid {
string uuid = 1;
}
message Empty {}

View File

@@ -0,0 +1,14 @@
import { Injectable, ValidationPipe } from '@nestjs/common';
import { RpcException } from '@nestjs/microservices';
@Injectable()
export class RpcValidationPipe extends ValidationPipe {
createExceptionFactory() {
return (validationErrors = []) => {
return new RpcException({
code: 3,
message: this.flattenValidationErrors(validationErrors),
});
};
}
}

View File

@@ -0,0 +1,9 @@
import { AutoMap } from '@automapper/classes';
export class UsernamePresenter {
@AutoMap()
uuid: string;
@AutoMap()
username: string;
}

View File

@@ -0,0 +1,14 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import { IMessageBroker } from '../../domain/interfaces/message-broker';
@Injectable()
export class AuthenticationMessager extends IMessageBroker {
constructor(private readonly _amqpConnection: AmqpConnection) {
super('auth');
}
publish(routingKey: string, message: string): void {
this._amqpConnection.publish(this.exchange, routingKey, message);
}
}

View File

@@ -0,0 +1,8 @@
import { Injectable } from '@nestjs/common';
import { AuthRepository } from '../../../database/src/domain/auth-repository';
import { Authentication } from '../../domain/entities/authentication';
@Injectable()
export class AuthenticationRepository extends AuthRepository<Authentication> {
protected _model = 'auth';
}

View File

@@ -0,0 +1,14 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import { IMessageBroker } from '../../domain/interfaces/message-broker';
@Injectable()
export class LoggingMessager extends IMessageBroker {
constructor(private readonly _amqpConnection: AmqpConnection) {
super('logging');
}
publish(routingKey: string, message: string): void {
this._amqpConnection.publish(this.exchange, routingKey, message);
}
}

View File

@@ -0,0 +1,8 @@
import { Injectable } from '@nestjs/common';
import { AuthRepository } from '../../../database/src/domain/auth-repository';
import { Username } from '../../domain/entities/username';
@Injectable()
export class UsernameRepository extends AuthRepository<Username> {
protected _model = 'username';
}