mirror of
https://github.com/dadosfera/maestro.git
synced 2026-09-15 20:54:48 +00:00
302 lines
9.6 KiB
Markdown
302 lines
9.6 KiB
Markdown
<p align="center">
|
|
<image src="./assets/maestro.svg" style="width:10rem">
|
|
</p>
|
|
|
|
# Maestro
|
|
Maestro is the Dadosfera's gateway, it's responsible for the communication between the frontend application and Dadosfera's microservices.
|
|
|
|
## 🚀 Starting
|
|
These instructions will allow you to get a working copy of the project on your local machine for development and testing purposes.
|
|
|
|
### 📋 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<a id="installation"></a>
|
|
- Clone the repository
|
|
- SSH
|
|
```
|
|
git clone git@github.com:dadosfera/maestro.git
|
|
```
|
|
or
|
|
|
|
- HTTPS
|
|
```
|
|
git clone https://github.com/dadosfera/maestro.git
|
|
```
|
|
|
|
- Select the correct node version (optional, only if using [NVM](https://github.com/nvm-sh/nvm)):
|
|
```sh
|
|
nvm use
|
|
```
|
|
|
|
- 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 };
|
|
}
|
|
}
|
|
```
|
|
|
|
```ts
|
|
import { Authenticated } from '../../authentication/authentication.decorator';
|
|
|
|
@Controller('foo')
|
|
class FooController {
|
|
/* ... */
|
|
|
|
@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 };
|
|
}
|
|
}
|
|
```
|
|
|
|
### `@RequireAllPermissions`
|
|
This decorator requires that **all permissions** listed are granted to the requesting user.
|
|
|
|
```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() {
|
|
/* ... */
|
|
}
|
|
}
|
|
```
|
|
|
|
### `@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() {
|
|
/* ... */
|
|
}
|
|
}
|
|
```
|
|
|
|
### `@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
|
|
- The old authentication method placed the user data in the `request.body.info` field, this imposes certain issues regarding the request body because this data should be from the frontend without any modification by Maestro. Now this usage is ⚠️ **DEPRECATED** ⚠️. We are working to migrate to the `@User` parameter decorator. The old method is working while the migration is in progress.
|
|
|
|
- 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: `<type>[optional scope]: <description>`
|
|
|
|
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).
|