diff --git a/README.md b/README.md index 890cecc..91c5b4f 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,299 @@ -# Maestro

-

Maestro

-This is the Dadosfera´s gateway repository, it´s responsable for the communication between frontend application and Dadosfera´s mirosservices. -## 💻 Requirements +# Maestro +Maestro is the Dadosfera's gateway, it's responsible for the communication between the frontend application and Dadosfera's microservices. -Before you start, make sure you have done the following steps: +## 🚀 Starting +These instructions will allow you to get a working copy of the project on your local machine for development and testing purposes. -* Installed Nodejs version 16.14.2 -* Installed latest NPM version +### 📋 Requirements +- [NodeJS v18.3.0 LTS / NPM v8.11](https://nodejs.org/pt-br/download/) (you can opt to use [NVM](https://github.com/nvm-sh/nvm) to easily manage node versions) +- Request access to AWS Console dev account for **all services** (avoid gradually asking for each needed service. it will slow down your development cycle) + - Create your Access Key on the "Security credentials" menu + - Set the Access Key on your local development machine +- Request access to the dev, stg and prd VPNs +### 🔧 Installation +- Clone the repository + - SSH + ``` + git clone git@github.com:dadosfera/maestro.git + ``` + or + - HTTPS + ``` + git clone https://github.com/dadosfera/maestro.git + ``` -## 🚀 Installing Maestro +- Select the correct node version (optional, only if using [NVM](https://github.com/nvm-sh/nvm)): + ```sh + nvm use + ``` -First of all clone the repository: - * SSH: -``` -git clone git@github.com:dadosfera/maestro.git -``` - * HTTPS: -``` -git clone git@github.com:dadosfera/maestro.git +- Install the project dependencies: + ```sh + npm i + ``` + +- Setup the following enviroment variables: + ``` + ENV= + DUC_URL= + INFACTORY_URL= + OTFACTORY_URL= + PIFACTORY_URL= + SM_OAUTH_PATH= + ``` + +- Start the server: + ```sh + # dev mode + npm run start:dev + + # or in debug mode + npm run start:debug + ``` + The service should start successfully. + +## Authentication decorators +Maestro have utilities to ease the user authentication on every controller and route. The following decorators are available: + +### `@Authenticated` +If the user must be authenticated to make request, we can use the `@Authenticated` decorator in the controller or route, as needed. + +```ts +import { Authenticated } from '../../authentication/authentication.decorator'; + +@Controller('foo') +@Authenticated() +class FooController { + /* ... */ + + @Get('bar') + async getBar() { + this.logger.info('user is authenticated!'); + + return { authenticated: true }; + } +} ``` -## Enviroment variables +```ts +import { Authenticated } from '../../authentication/authentication.decorator'; -Here is a list of enviroment variables needed in order to run the application correctly. +@Controller('foo') +class FooController { + /* ... */ -``` -ENV= -DUC_URL= -INFACTORY_URL= -TRFACTORY_URL= -OTFACTORY_URL= -PIFACTORY_URL= -JWT_PRIVATE_KEY= -AWS_IDENTITY_POOL_ID= + @Get('bar') + @Authenticated() + async getBar() { + this.logger.info('user is authenticated!'); + + return { authenticated: true }; + } + + @Post('bar') + async postBar() { + this.logger.info('user is NOT authenticated!'); + + return { authenticated: false }; + } +} ``` -## Running Maestro +### `@RequireAllPermissions` +This decorator requires that **all permissions** listed are granted to the requesting user. -In order to run Maestro just run the following command: +```ts +import { RequireAllPermissions } from '../../authentication/authentication.decorator'; +import { Permissions } from '../../authentication/permissions.enum'; + +@Controller('foo') +class FooController { + /* ... */ + + @Get('bar') + @RequireAllPermissions(Permissions.BAR.MANAGE, Permissions.BAR.CREATE) + async getBar() { + /* ... */ + } +} ``` -npm run start:dev + +### `@RequireSomePermission` +In the following case, the user is required to have **at least one** listed permission. + +```ts +import { RequireSomePermission } from '../../authentication/authentication.decorator'; +import { Permissions } from '../../authentication/permissions.enum'; + +@Controller('foo') +@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.CREATE) +class FooController { + /* ... */ + + @Get('bar') + async getBar() { + /* ... */ + } +} ``` -If everything is fine the Maestro will start and be ready to receive HTTP requests \ No newline at end of file + +### `@AuthenticateCondition` +If a more complicated authentication check needs to be done, we can use the `@AuthenticateCondition` decorator to define it. The custom function must return `true` to authenticate the request. + +In the following example: +- all routes on the `FooController` controller can only be requested from localhost +- `POST /foo/bar` can only be requested from localhost **and** by users from customer id `111...eef` + +```ts +import { AuthenticateCondition } from '../../authentication/authentication.decorator'; + +@Controller('foo') +// allow requests only from localhost +@AuthenticateCondition((request: Request) => request.ip === '::ffff:127.0.0.1') +class FooController { + /* ... */ + + @Get('bar') + async getBar() { + /* ... */ + } + + @Post('bar') + // allow requests only from a specific customer + @AuthenticateCondition((request: Request, user: RequestUser) => user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef') + async postBar() { + /* ... */ + } +} +``` + +### Note on authentication decorators +- Authentication decorators can be used together and all of them **must** pass to the request be authenticated, but in the general case you don't need to (*and wouldn't like to...*) use all of them together, as you can code all of the authentication logic in the `@AuthenticateCondition` decorator. + + ```ts + import { + RequireAllPermissions, + RequireSomePermission, + AuthenticateCondition, + } from '../../authentication/authentication.decorator'; + import { Permissions } from '../../authentication/permissions.enum'; + import { Waa, Baz } from './authenticationFunctions' + + @Controller('foo') + @RequireAllPermissions(Permissions.FOO.USE, Permissions.FOO.REQUEST) + @RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.ADMIN) + @AuthenticateCondition(Waa) + @AuthenticateCondition(Baz) + class FooController { + /* ... */ + + @Get('bar') + async getBar() { + /* ... */ + } + + @Post('bar') + @RequireAllPermissions(Permissions.BAR.MANAGE, Permissions.BAR.USE, Permissions.BAR.REQUEST) + async postBar() { + /* ... */ + } + } + ``` + +- If `@RequireAllPermissions` and `@RequireSomePermission` are used with **only a single permission**, they present the **exactly same behavior**. + + ```ts + // same behavior + @RequireAllPermissions(Permissions.BAR.MANAGE) + @RequireSomePermission(Permissions.BAR.MANAGE) + ``` + + + +## `@User` parameter decorator +The requesting user data can be obtained using the @User parameter decorator, like in the following snippet: + +```ts +import { User, RequestUser } from '../../authentication/user.decorator'; + +@Controller('foo') +class FooController { + /* ... */ + + @Get('bar') + async getBar(@User() user: RequestUser) { + this.logger.info(user); + + return { user }; + } +} +``` + +If the user is required to be logged in, set `required` to `true`, as you would want in the `change-password` operation: + +```ts +import { User, RequestUser } from '../../authentication/user.decorator'; + +@Controller('user') +class UserController { + /* ... */ + + @Post('change-password') + @HttpCode(HttpStatus.OK) + async changePassword( + @User({ required: true }) user: RequestUser, + @Body() body: AuthChangePasswordRequest, + ) { + const { oldPassword, newPassword } = body; + + return this.authClient.changePassword({ + userId: user.user_id, + oldPassword, + newPassword, + }); + } +} +``` + +## 📦 Development +### ⌨️ Coding Style +By default, we use [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) with default settings. + +**We recommend using Visual Studio Code and installing the recommended extensions to ease the development process.** + +### Commits pattern +Our workflow pipeline follows the conventional commits specs ([cheat sheets](https://cheatography.com/albelop/cheat-sheets/conventional-commits/)) to release versions accordingly. + +Format: `[optional scope]: ` + +Example: `FIX: ensure Range headers adhere more closely to RFC 2616` + +### Branching naming convention +- **Feature**: Any code changes for a new module or use case should be done on a feature branch. This branch is created based on the `main` branch. When all changes are done, a Pull Request/Merge Request is needed to put all of these changes back to the `main` branch. Examples: `feature/integrate-swagger`, `feature/JIRA-1234`, `feature/JIRA-1234_support-dark-theme`. + + **It is recommended to use all lower caps letters and hyphen (-) to separate words unless it is a specific item name or ID. Underscore (_) could be used to separate the ID and description.** + +- **Bug Fix**: If the code changes made from the feature branch were rejected after a release, sprint or demo, any necessary fixes after that should be done on the bugfix branch. Examples: `bugfix/more-gray-shades`, `bugfix/JIRA-1444_gray-on-blur-fix`. + +- **Hot Fix**: If there is a need to fix a blocker, do a temporary patch, apply a critical framework or configuration change that should be handled immediately, it should be created as a Hotfix. Examples: `hotfix/disable-endpoint-zero-day-exploit`, `hotfix/increase-scaling-threshold`. + +- **Experimental**: A branch for playing around. Any new feature or idea that is not part of a release or a sprint. Example: `experimental/dark-theme-support`. + +### Making a Pull Request +1. Commit your changes +2. Open the Pull Request on GitHub +3. Send Pull Request link in microsfera Google Chat Group for review and possible approval + +## 🛠️ Built with +Some technologies used in this project: + +- [NestJS](https://docs.nestjs.com) - Framework for building efficient and scalable NodeJS server-side applications + +## ⚙️ Back-end Architecture +The architecture can be found at [this link](https://sites.google.com/dadosfera.ai/wikidoproduto/time/back-end).