Compare commits

..
Author SHA1 Message Date
Gabriel Amorim a722fc6c86 WIP: new logic for feature request 2023-05-05 12:12:59 -03:00
32 changed files with 3947 additions and 5023 deletions
+6 -28
View File
@@ -13,11 +13,6 @@ on:
- stg - stg
- stg2 - stg2
- prd - prd
push_to_dockerhub:
description: "Push image to Dockerhub?"
required: true
type: boolean
default: false
jobs: jobs:
extract_environment: extract_environment:
@@ -44,15 +39,15 @@ jobs:
new_release_version: ${{ (steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version) || (github.event_name == 'workflow_dispatch' && '0.0.0') }} new_release_version: ${{ (steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version) || (github.event_name == 'workflow_dispatch' && '0.0.0') }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- if: github.event_name != 'workflow_dispatch' - if: github.event_name != 'workflow_dispatch'
name: Semantic Release name: Semantic Release
uses: cycjimmy/semantic-release-action@v4 uses: cycjimmy/semantic-release-action@v3
id: semantic id: semantic
with: with:
extra_plugins: | extra_plugins: |
conventional-changelog-eslint@4.0.0 conventional-changelog-eslint
branches: | branches: |
[ [
'main' 'main'
@@ -81,7 +76,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Update Pip - name: Update Pip
run: | run: |
@@ -100,14 +95,14 @@ jobs:
python3 -m pip install awsebcli --upgrade python3 -m pip install awsebcli --upgrade
- name: Configure AWS Region - name: Configure AWS Region
uses: aws-actions/configure-aws-credentials@v4 uses: aws-actions/configure-aws-credentials@v1-node16
id: aws id: aws
with: with:
aws-region: us-east-1 aws-region: us-east-1
- name: Login to AWS ECR - name: Login to AWS ECR
id: login_ecr id: login_ecr
uses: aws-actions/amazon-ecr-login@v2 uses: aws-actions/amazon-ecr-login@v1
- name: Build, Tag, and Push Image to AWS ECR - name: Build, Tag, and Push Image to AWS ECR
env: env:
@@ -118,23 +113,6 @@ jobs:
docker-compose -f build.docker-compose.yml build docker-compose -f build.docker-compose.yml build
docker-compose -f build.docker-compose.yml push docker-compose -f build.docker-compose.yml push
- name: Login to Docker Hub
if: ${{inputs.push_to_dockerhub}}
uses: docker/login-action@v2
with:
username: dadosfera
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build, Tag, and Push Image to Dockerhub
if: ${{inputs.push_to_dockerhub}}
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
run: |
docker-compose -f build.docker-compose.dockerhub.yml build
docker-compose -f build.docker-compose.dockerhub.yml push
- name: Create ZIP file to Deploy AWS Beanstalk - name: Create ZIP file to Deploy AWS Beanstalk
env: env:
ENV: ${{ needs.extract_environment.outputs.environment }} ENV: ${{ needs.extract_environment.outputs.environment }}
+1 -1
View File
@@ -1 +1 @@
18.10.0 18.3.0
+1 -2
View File
@@ -12,7 +12,6 @@
"start:debug" "start:debug"
], ],
"runtimeExecutable": "npm", "runtimeExecutable": "npm",
"runtimeVersion": "18.10.0",
"skipFiles": [ "skipFiles": [
"<node_internals>/**" "<node_internals>/**"
], ],
@@ -20,4 +19,4 @@
"console": "integratedTerminal" "console": "integratedTerminal"
} }
] ]
} }
+3 -3
View File
@@ -1,5 +1,5 @@
# install all dependencies # install all dependencies
FROM node:18.10-alpine as packages FROM node:18.3.0-alpine3.15 as packages
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
RUN apk add --no-cache aws-cli \ RUN apk add --no-cache aws-cli \
@@ -7,12 +7,12 @@ RUN apk add --no-cache aws-cli \
&& npm install && npm install
# unit test specific build # unit test specific build
FROM node:18.10-alpine as test FROM node:18.3.0-alpine3.15 as test
WORKDIR /app WORKDIR /app
COPY --from=packages /app/node_modules ./node_modules COPY --from=packages /app/node_modules ./node_modules
COPY . ./ COPY . ./
FROM node:18.10-alpine FROM node:18.3.0-alpine3.15
WORKDIR /app WORKDIR /app
COPY . /app/ COPY . /app/
COPY --from=packages /app/node_modules ./node_modules COPY --from=packages /app/node_modules ./node_modules
-10
View File
@@ -1,10 +0,0 @@
LICENSE
Copyright (C) 2023 Dadosfera
All rights reserved.
This software and related documentation are provided under a license agreement containing restrictions on use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means.
Reverse engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is prohibited.
If you have any questions about this, please contact Dadosfera.ai at legal@dadosfera.ai
+1 -1
View File
@@ -12,7 +12,7 @@ These instructions will allow you to get a working copy of the project on your l
### 📋 Requirements ### 📋 Requirements
- [NodeJS v18.10.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) - [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) - 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 - Create your Access Key on the "Security credentials" menu
- Set the Access Key on your local development machine - Set the Access Key on your local development machine
-5
View File
@@ -1,5 +0,0 @@
version: "3.8"
services:
maestro:
build: .
image: dadosfera/maestro_${ENV}:${IMAGE_TAG}
-29
View File
@@ -1,29 +0,0 @@
version: "3.8"
services:
maestro:
image: dadosfera/maestro_${ENV}:${IMAGE_TAG}
container_name: maestro
restart: always
ports:
- 3333:3333
env_file:
- .env
node_exporter:
image: ${NODE_EXPORTER_URL}
container_name: node_exporter
restart: always
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
- /run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
- '--collector.systemd'
- '--collector.processes'
network_mode: host
pid: host
+1 -1
View File
@@ -1752,7 +1752,7 @@
} }
}, },
"info": { "info": {
"title": "Maestro - feat/generate-token", "title": "Maestro - feature/customer-links",
"description": "This is the Maestro API", "description": "This is the Maestro API",
"version": "1.0.0", "version": "1.0.0",
"contact": {} "contact": {}
+52 -121
View File
@@ -542,67 +542,6 @@
] ]
} }
}, },
"/auth/oauth/magalu-id": {
"get": {
"operationId": "AuthController_magaluIdOauth",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"Auth"
]
}
},
"/auth/oauth/magalu-id/callback": {
"get": {
"operationId": "AuthController_magaluIdOauthCallback",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Auth"
]
}
},
"/connections": { "/connections": {
"post": { "post": {
"operationId": "ConnectionController_createConnection", "operationId": "ConnectionController_createConnection",
@@ -1473,6 +1412,51 @@
] ]
} }
}, },
"/users/connector_request": {
"post": {
"operationId": "UsersController_sendConnectorRequest",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateUserRes"
}
}
}
},
"201": {
"description": ""
}
},
"tags": [
"Users"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/roles": { "/roles": {
"get": { "get": {
"operationId": "RolesController_searchRoles", "operationId": "RolesController_searchRoles",
@@ -4984,14 +4968,7 @@
}, },
"responses": { "responses": {
"201": { "201": {
"description": "", "description": ""
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
} }
}, },
"tags": [ "tags": [
@@ -5010,14 +4987,7 @@
"parameters": [], "parameters": [],
"responses": { "responses": {
"201": { "201": {
"description": "", "description": ""
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
} }
}, },
"security": [ "security": [
@@ -5047,6 +5017,9 @@
"Customers" "Customers"
], ],
"security": [ "security": [
{
"access-token": []
},
{ {
"access-token": [] "access-token": []
} }
@@ -5090,44 +5063,6 @@
] ]
} }
}, },
"/customers/token": {
"get": {
"operationId": "CustomersController_getCustomerToken",
"parameters": [
{
"name": "exp",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
}
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/health": { "/health": {
"get": { "get": {
"operationId": "HealthController_check", "operationId": "HealthController_check",
@@ -5144,7 +5079,7 @@
} }
}, },
"info": { "info": {
"title": "Maestro - ci/dockerhub", "title": "Maestro - main",
"description": "This is the Maestro API", "description": "This is the Maestro API",
"version": "1.0.0", "version": "1.0.0",
"contact": {} "contact": {}
@@ -5368,15 +5303,11 @@
}, },
"accessToken": { "accessToken": {
"type": "string" "type": "string"
},
"refreshToken": {
"type": "string"
} }
}, },
"required": [ "required": [
"permissions", "permissions",
"accessToken", "accessToken"
"refreshToken"
] ]
}, },
"CreateConnectionDto": { "CreateConnectionDto": {
-1
View File
@@ -4,7 +4,6 @@ declare global {
NODE_ENV: 'test'; NODE_ENV: 'test';
ENV: 'local' | 'stg' | 'prd' | 'test'; ENV: 'local' | 'stg' | 'prd' | 'test';
LOCAL_ENV: 'stg' | 'prd'; LOCAL_ENV: 'stg' | 'prd';
CLOUD_ENVIRONMENT: 'aws' | 'gcp' | 'mgc';
DUC_URL: string; DUC_URL: string;
INFACTORY_URL: string; INFACTORY_URL: string;
+3758 -4469
View File
File diff suppressed because it is too large Load Diff
+18 -18
View File
@@ -6,7 +6,7 @@
"private": true, "private": true,
"license": "UNLICENSED", "license": "UNLICENSED",
"engines": { "engines": {
"node": "18.10.0" "node": "18.3.0"
}, },
"scripts": { "scripts": {
"preinstall": "aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1", "preinstall": "aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1",
@@ -29,23 +29,21 @@
"dependencies": { "dependencies": {
"@aws-sdk/client-secrets-manager": "^3.112.0", "@aws-sdk/client-secrets-manager": "^3.112.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack-v2": "3.32.0-beta.2", "@dadosfera/protospack-v2": "3.31.0",
"@grpc/grpc-js": "^1.6.7", "@grpc/grpc-js": "^1.6.7",
"@grpc/proto-loader": "^0.6.13", "@grpc/proto-loader": "^0.6.13",
"@nestjs/cli": "^9.4.2", "@nestjs/common": "^8.4.7",
"@nestjs/common": "^9.4.0", "@nestjs/config": "^1.2.1",
"@nestjs/config": "^2.3.1", "@nestjs/core": "^8.4.7",
"@nestjs/core": "^9.4.0", "@nestjs/mapped-types": "*",
"@nestjs/mapped-types": "^1.2.2", "@nestjs/microservices": "^8.4.7",
"@nestjs/microservices": "^9.4.0", "@nestjs/passport": "^8.2.2",
"@nestjs/passport": "^9.0.3", "@nestjs/platform-express": "^8.4.7",
"@nestjs/platform-express": "^9.4.0", "@nestjs/schedule": "^1.1.0",
"@nestjs/schematics": "^9.1.0", "@nestjs/swagger": "^5.2.1",
"@nestjs/swagger": "^6.3.0", "axios": "^0.27.2",
"@nestjs/testing": "^9.4.0",
"axios": "^1.6.2",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.0", "class-validator": "^0.13.2",
"cron-parser": "^4.4.0", "cron-parser": "^4.4.0",
"dotenv": "^14.3.2", "dotenv": "^14.3.2",
"elastic-apm-node": "^3.36.0", "elastic-apm-node": "^3.36.0",
@@ -53,7 +51,6 @@
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"jwk-to-pem": "^2.0.5", "jwk-to-pem": "^2.0.5",
"mixpanel": "^0.17.0", "mixpanel": "^0.17.0",
"ms": "^3.0.0-canary.1",
"passport": "^0.6.0", "passport": "^0.6.0",
"passport-facebook": "^3.0.0", "passport-facebook": "^3.0.0",
"passport-forcedotcom": "^0.2.0", "passport-forcedotcom": "^0.2.0",
@@ -70,6 +67,9 @@
"multer": "1.4.5-lts.1" "multer": "1.4.5-lts.1"
}, },
"devDependencies": { "devDependencies": {
"@nestjs/cli": "^8.2.8",
"@nestjs/schematics": "^8.0.11",
"@nestjs/testing": "^8.4.7",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/jest": "27.0.2", "@types/jest": "27.0.2",
"@types/jsonwebtoken": "^8.5.8", "@types/jsonwebtoken": "^8.5.8",
@@ -80,8 +80,8 @@
"@types/passport-google-oauth20": "^2.0.11", "@types/passport-google-oauth20": "^2.0.11",
"@types/passport-oauth2": "^1.4.11", "@types/passport-oauth2": "^1.4.11",
"@types/supertest": "^2.0.12", "@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.35.0", "@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.35.0", "@typescript-eslint/parser": "^5.29.0",
"eslint": "^8.18.0", "eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
@@ -44,7 +44,7 @@ class NoClassAuthController {
@Get('has-all-permissions') @Get('has-all-permissions')
@RequireAllPermissions( @RequireAllPermissions(
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN, PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE, PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
) )
async hasAllPermissions(@Body() body) { async hasAllPermissions(@Body() body) {
return { body }; return { body };
@@ -53,7 +53,7 @@ class NoClassAuthController {
@Get('has-some-permission') @Get('has-some-permission')
@RequireSomePermission( @RequireSomePermission(
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN, PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE, PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
) )
async hasSomePermission(@Body() body) { async hasSomePermission(@Body() body) {
return { body }; return { body };
@@ -62,7 +62,7 @@ class NoClassAuthController {
@Controller('class-auth-condition') @Controller('class-auth-condition')
@AuthenticateCondition((req) => req.get('x-on-class') === 'ok') @AuthenticateCondition((req) => req.get('x-on-class') === 'ok')
@RequireSomePermission(PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE) @RequireSomePermission(PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE)
class ClassAuthConditionController { class ClassAuthConditionController {
@Get('body') @Get('body')
async getBody(@Body() body) { async getBody(@Body() body) {
@@ -444,13 +444,13 @@ describe('authentication.guard', () => {
NoClassAuthTest(tokenZ, ['zendesk']); NoClassAuthTest(tokenZ, ['zendesk']);
ClassAuthConditionTest(tokenZ, ['zendesk']); ClassAuthConditionTest(tokenZ, ['zendesk']);
const tokenM = CreateToken([PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE]); const tokenM = CreateToken([PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE]);
NoClassAuthTest(tokenM, ['metabase']); NoClassAuthTest(tokenM, ['metabase']);
ClassAuthConditionTest(tokenM, ['metabase']); ClassAuthConditionTest(tokenM, ['metabase']);
const tokenZM = CreateToken([ const tokenZM = CreateToken([
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN, PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE, PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
]); ]);
NoClassAuthTest(tokenZM, ['zendesk', 'metabase']); NoClassAuthTest(tokenZM, ['zendesk', 'metabase']);
ClassAuthConditionTest(tokenZM, ['zendesk', 'metabase']); ClassAuthConditionTest(tokenZM, ['zendesk', 'metabase']);
+1 -5
View File
@@ -79,13 +79,9 @@ export class AuthenticationGuard
request, request,
mustBeAuthenticated: boolean, mustBeAuthenticated: boolean,
): RequestUser | false { ): RequestUser | false {
const accessToken: string = request.get('Authorization'); const accessToken = request.get('Authorization');
let accessTokenPayload: RequestUser; let accessTokenPayload: RequestUser;
this.logger.info(`mustBeAuthenticated: ${mustBeAuthenticated}`);
this.logger.info(
`accessToken (first 5 chars): ${accessToken?.slice(0, 5)}`,
);
// If the user isn't authenticated, an error will occurr anywhere here. // If the user isn't authenticated, an error will occurr anywhere here.
// Fancy error avoidance isn't performed by purpose, such as avoiding to access null values. // Fancy error avoidance isn't performed by purpose, such as avoiding to access null values.
try { try {
+2 -22
View File
@@ -55,16 +55,6 @@ export const PERMISSIONS_GROUPS = {
'es-es': '', 'es-es': '',
}, },
}, },
GENERATE_TOKEN: {
seqid: 46,
claim: 'customer:generate-token',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Gerar Token de Acesso',
'en-us': 'Generate Acess Token',
'es-es': 'Gerar Token de Acceso',
},
},
}, },
}, },
@@ -459,7 +449,7 @@ export const PERMISSIONS_GROUPS = {
}, },
}, },
DATAVIZ: { ANALYZE: {
title: { title: {
'pt-br': 'Analisar | Visualização', 'pt-br': 'Analisar | Visualização',
'en-us': 'Analyze | Visualization', 'en-us': 'Analyze | Visualization',
@@ -476,15 +466,6 @@ export const PERMISSIONS_GROUPS = {
'es-es': 'Acceso Metabase', 'es-es': 'Acceso Metabase',
}, },
}, },
},
},
INTELLIGENCE: {
title: {
'pt-br': 'Analisar | Inteligência',
'en-us': 'Analyze | Intelligence',
'es-es': 'Analizar | Inteligencia',
},
permissions: {
INTELLIGENCE: { INTELLIGENCE: {
seqid: 31, seqid: 31,
claim: 'intelligence:open', claim: 'intelligence:open',
@@ -606,8 +587,7 @@ export const DADOSFERA_MODULES: Array<DadosferaModule> = [
name: 'Intelligence Module', name: 'Intelligence Module',
description: 'Orchest Module', description: 'Orchest Module',
key: 'intelligence', key: 'intelligence',
permissionSeqId: permissionSeqId: PERMISSIONS_GROUPS.ANALYZE.permissions.INTELLIGENCE.seqid,
PERMISSIONS_GROUPS.INTELLIGENCE.permissions.INTELLIGENCE.seqid,
}, },
{ {
name: 'Proccessing Module', name: 'Proccessing Module',
+1 -1
View File
@@ -46,7 +46,7 @@ describe('user.decorator', () => {
const fakeUserPayload = { const fakeUserPayload = {
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125', user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
username: 'super.admin', username: 'super.admin',
permissions: [PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE].map( permissions: [PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE].map(
({ seqid }) => seqid, ({ seqid }) => seqid,
), ),
customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11', customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11',
+28 -92
View File
@@ -52,7 +52,6 @@ import jwt from 'jsonwebtoken';
import { LanguageEnum } from 'src/utils/languages.enum'; import { LanguageEnum } from 'src/utils/languages.enum';
import { Language } from 'src/decorators/language.decorator'; import { Language } from 'src/decorators/language.decorator';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator'; import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
import { getFrontendUrl } from 'src/utils/getFrontendBaseUrl';
@ApiTags('Auth') @ApiTags('Auth')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }]) @ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@@ -60,7 +59,7 @@ import { getFrontendUrl } from 'src/utils/getFrontendBaseUrl';
@Controller('auth') @Controller('auth')
export class AuthController { export class AuthController {
logger: DadosferaLogger; logger: DadosferaLogger;
frontendRedirectUrl: string; redirectUrl: string;
constructor( constructor(
@Inject(DadosferaLogger) @Inject(DadosferaLogger)
@@ -68,7 +67,17 @@ export class AuthController {
private authClient: AuthClientService, private authClient: AuthClientService,
) { ) {
this.logger = dadosferaLogger.logger; this.logger = dadosferaLogger.logger;
this.frontendRedirectUrl = getFrontendUrl('/auth/login').href;
switch (process.env.ENV) {
case 'stg':
this.redirectUrl = `https://app.${process.env.ENV}.dadosfera.ai/auth/login`;
break;
case 'prd':
this.redirectUrl = `https://app.dadosfera.ai/auth/login`;
break;
default:
this.redirectUrl = `http://localhost:4200/auth/login`;
}
} }
@Post('sign-in') @Post('sign-in')
@@ -96,7 +105,7 @@ export class AuthController {
customer_name, customer_name,
language, language,
}); });
this.logger.info(`metadata: ${metadata}`);
return this.authClient.refreshAccessToken({ refreshToken }, metadata); return this.authClient.refreshAccessToken({ refreshToken }, metadata);
} }
@@ -260,13 +269,7 @@ export class AuthController {
@UseGuards(AuthGuard('google-login')) @UseGuards(AuthGuard('google-login'))
@Redirect() @Redirect()
async googleOauthCallback(@Req() req) { async googleOauthCallback(@Req() req) {
const { const { url, email, token, language = 'pt-br' } = await this.callback(req);
url,
email,
token,
refreshToken,
language = 'pt-br',
} = await this.callback(req);
if (url.searchParams.get('error')) { if (url.searchParams.get('error')) {
this.logger.error('/oauth/google - ERROR'); this.logger.error('/oauth/google - ERROR');
return { url: url.href }; return { url: url.href };
@@ -276,7 +279,6 @@ export class AuthController {
.oauthSignIn({ .oauthSignIn({
username: email, username: email,
token, token,
refreshToken,
}) })
.then(({ session }) => { .then(({ session }) => {
this.logger.info('/oauth/google - SUCESS'); this.logger.info('/oauth/google - SUCESS');
@@ -306,79 +308,18 @@ export class AuthController {
return { url: url.href }; return { url: url.href };
} }
@ApiInternalOnlyEndpoint() async callback(req: Request) {
@Get('oauth/magalu-id')
@UseGuards(AuthGuard('magalu-id-login'))
magaluIdOauth() {
this.logger.info('/oauth/magalu-id');
return true;
}
@ApiInternalOnlyEndpoint()
@Get('oauth/magalu-id/callback')
@UseGuards(AuthGuard('magalu-id-login'))
@Redirect()
async magaluIdOauthCallback(@Req() req) {
const {
url,
email,
token,
refreshToken,
language = 'pt-br',
} = await this.callback(req, 'magalu-id');
if (url.searchParams.get('error')) {
this.logger.error('/oauth/magalu-id/callback - ERROR');
this.logger.error(url.searchParams.get('error'));
return { url: url.href };
}
await this.authClient
.oauthSignIn({
username: email,
token,
refreshToken,
})
.then(({ session }) => {
this.logger.info('/oauth/magalu-id/callback - SUCESS');
url.searchParams.set('session', session);
})
.catch((err) => {
this.logger.error('/oauth/magalu-id/callback - LOGIN ERROR');
let error = OauthErrors.INVALID_CREDENTIALS[language].error;
let error_description =
OauthErrors.INVALID_CREDENTIALS[language].error_description;
switch (err.details) {
case ErrorCodes.USER.NOT_FOUND:
error = OauthErrors.USER_NOT_FOUND[language].error;
error_description =
OauthErrors.USER_NOT_FOUND[language].error_description(email);
break;
case ErrorCodes.AUTH.UNAUTHORIZED:
error = OauthErrors.INVALID_SESSION[language].error;
error_description =
OauthErrors.INVALID_SESSION[language].error_description;
break;
}
url.searchParams.set('error', error);
url.searchParams.set('error_description', error_description);
return null;
});
return { url: url.href };
}
async callback(req: Request, oauth_type?: 'google' | 'magalu-id') {
const { error, state } = req.query; const { error, state } = req.query;
const { authInfo } = req; const { authInfo } = req;
const url = new URL(this.frontendRedirectUrl); const url = new URL(this.redirectUrl);
let email, token, refreshToken, error_title, error_description; let email, token, error_title, error_description;
const language: 'pt-br' | 'en-us' = 'pt-br'; let language: 'pt-br' | 'en-us' = 'pt-br';
// let stateObject;
// try { const stateObject = jwt.verify(
// stateObject = jwt.verify(state as string, process.env.JWT_PRIVATE_KEY); state as string,
// } catch (error) { process.env.JWT_PRIVATE_KEY,
// console.log('error', error); );
// } if (typeof stateObject != 'string') language = stateObject.language;
// if (typeof stateObject != 'string') language = stateObject.language;
if (error || !authInfo) { if (error || !authInfo) {
this.logger.error(error); this.logger.error(error);
@@ -389,16 +330,11 @@ export class AuthController {
OauthErrors.INVALID_CREDENTIALS[language].error_description; OauthErrors.INVALID_CREDENTIALS[language].error_description;
if (error) error_description += ` - [${error}]`; if (error) error_description += ` - [${error}]`;
} else { } else {
const { accessToken, refreshToken: rt } = authInfo as any; const { accessToken } = authInfo as any;
const { _json: userInfo = {} } = req.user as any; const { _json: userInfo } = req.user as any;
email = userInfo.email; email = userInfo.email;
token = accessToken; token = accessToken;
refreshToken = rt;
if (oauth_type === 'magalu-id') {
const jwtDecoded = jwt.decode(token, { json: true });
email = jwtDecoded.email;
}
} }
if (error_title) { if (error_title) {
@@ -406,6 +342,6 @@ export class AuthController {
url.searchParams.set('error_description', error_description); url.searchParams.set('error_description', error_description);
} }
return { token, refreshToken, email, url, language }; return { token, email, url, language };
} }
} }
-2
View File
@@ -8,7 +8,6 @@ import { AuthClientService } from './auth.service';
import { DucClient } from '../duc/client.config'; import { DucClient } from '../duc/client.config';
import { GoogleLoginStrategy } from './passport-strategies/google-strategy'; import { GoogleLoginStrategy } from './passport-strategies/google-strategy';
import { getOauthSecrets } from 'src/utils/OauthSecrets'; import { getOauthSecrets } from 'src/utils/OauthSecrets';
import { MagaluIdStrategy } from './passport-strategies/magalu-id-strategy';
const client = new DucClient(); const client = new DucClient();
@Module({ @Module({
@@ -18,7 +17,6 @@ const client = new DucClient();
AuthClientService, AuthClientService,
DadosferaLogger, DadosferaLogger,
GoogleLoginStrategy, GoogleLoginStrategy,
MagaluIdStrategy,
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() }, { provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
], ],
exports: [AuthClientService], exports: [AuthClientService],
+3 -3
View File
@@ -17,7 +17,6 @@ import {
AuthResetPasswordRequest, AuthResetPasswordRequest,
AuthVerifyResetPasswordCodeRequest, AuthVerifyResetPasswordCodeRequest,
AuthConfirmResetPasswordRequest, AuthConfirmResetPasswordRequest,
AuthOauthSignInRequest,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages'; } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { DucClient } from '../duc/client.config'; import { DucClient } from '../duc/client.config';
import { Metadata } from '@grpc/grpc-js'; import { Metadata } from '@grpc/grpc-js';
@@ -163,7 +162,8 @@ export class AuthClientService implements OnModuleInit {
return lastValueFrom(this.authService.AuthGetSession({ session })); return lastValueFrom(this.authService.AuthGetSession({ session }));
} }
async oauthSignIn(data: AuthOauthSignInRequest) { async oauthSignIn(data: { username: string; token: string }) {
return lastValueFrom(this.authService.AuthOauthSignIn(data)); const { username, token } = data;
return lastValueFrom(this.authService.AuthOauthSignIn({ token, username }));
} }
} }
-2
View File
@@ -117,6 +117,4 @@ export class AuthRefreshAccessTokenRes {
permissions: string[]; permissions: string[];
@ApiProperty() @ApiProperty()
accessToken: string; accessToken: string;
@ApiProperty()
refreshToken: string;
} }
@@ -1,47 +0,0 @@
import {
Strategy as Oauth2Strategy,
StrategyOptions,
VerifyCallback,
VerifyFunction,
} from 'passport-oauth2';
import { PassportStrategy } from '@nestjs/passport';
import { Inject, Injectable } from '@nestjs/common';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { Request } from 'express';
import jwt from 'jsonwebtoken';
@Injectable()
export class MagaluIdStrategy extends PassportStrategy(
Oauth2Strategy,
'magalu-id-login',
) {
redirect_uri: string;
constructor() {
const options: StrategyOptions = {
clientID: process.env.MAGALU_ID_CLIENT_ID,
clientSecret: process.env.MAGALU_ID_CLIENT_SECRET,
callbackURL: process.env.MAGALU_ID_CALLBACK_URL,
scope: ['openid'],
authorizationURL: 'https://id.magalu.com/login',
tokenURL: 'https://id.magalu.com/oauth/token',
};
const verify: VerifyFunction = (
accessToken: string,
refreshToken: string,
profile: any,
verified: VerifyCallback,
) => {
return verified(null, profile, { accessToken, refreshToken });
};
super(options, verify);
}
// authenticate(req: Request, options: Record<string, any>) {
// const language =
// req.headers['dadosfera-lang'] || req.query.language || 'pt-br';
// // options.state = jwt.sign({ language }, process.env.JWT_PRIVATE_KEY);
// super.authenticate(req, options);
// console.log('authenticate', JSON.stringify(options));
// }
}
+1 -7
View File
@@ -49,16 +49,10 @@ class CatalogService implements OnModuleInit {
_getNimbusUrl(body) { _getNimbusUrl(body) {
const customer = body.info.customer.toLowerCase(); const customer = body.info.customer.toLowerCase();
const cloud_environment = process.env.CLOUD_ENVIRONMENT || 'aws'; if (process.env.ENV === 'prd') {
if (process.env.ENV === 'prd' && cloud_environment === 'aws') {
return `https://nimbus-${customer}.dadosfera.ai`; return `https://nimbus-${customer}.dadosfera.ai`;
} }
if (process.env.ENV === 'prd' && cloud_environment === 'mgc') {
return `https://nimbus-${customer}.dadosfera.com`;
}
return `https://nimbus-${customer}.${process.env.ENV.replace( return `https://nimbus-${customer}.${process.env.ENV.replace(
'local', 'local',
'stg', 'stg',
+1 -18
View File
@@ -7,7 +7,6 @@ import {
Inject, Inject,
Param, Param,
Put, Put,
Query,
UseFilters, UseFilters,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
@@ -20,8 +19,6 @@ import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter'; import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { CustomersService } from './customers.service'; import { CustomersService } from './customers.service';
import { CustomerDto, CustomerLinksResponse } from './dtos/customers'; import { CustomerDto, CustomerLinksResponse } from './dtos/customers';
import { RequestUser, User } from 'src/decorators/user.decorator';
import type { StringValue } from 'ms';
@ApiInternalOnlyController() @ApiInternalOnlyController()
@ApiTags('Customers') @ApiTags('Customers')
@@ -40,6 +37,7 @@ export class CustomersController {
} }
@Get(':id/links') @Get(':id/links')
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
async getCustomerLinks(@Param('id') id): Promise<CustomerLinksResponse> { async getCustomerLinks(@Param('id') id): Promise<CustomerLinksResponse> {
this.logger.info('getCustomerLinks', { id }); this.logger.info('getCustomerLinks', { id });
const links = await this.customersService.getLinks(id); const links = await this.customersService.getLinks(id);
@@ -56,19 +54,4 @@ export class CustomersController {
this.logger.info('setCustomerLinks', { id, links }); this.logger.info('setCustomerLinks', { id, links });
return this.customersService.setLinks(id, links); return this.customersService.setLinks(id, links);
} }
@Get('token')
@RequireAllPermissions(PERMISSIONS_GROUPS.AUTH.permissions.GENERATE_TOKEN)
async getCustomerToken(
@Query('exp') exp: StringValue,
@User() user: RequestUser,
): Promise<string> {
this.logger.info('getCustomerToken', { exp, user });
const data = {
customerId: user.customer_id,
userId: user.user_id,
customerName: user.customer_name,
};
return this.customersService.generateToken(exp, data);
}
} }
@@ -4,7 +4,6 @@ import {
Injectable, Injectable,
HttpException, HttpException,
HttpStatus, HttpStatus,
InternalServerErrorException,
} from '@nestjs/common'; } from '@nestjs/common';
import { firstValueFrom, lastValueFrom } from 'rxjs'; import { firstValueFrom, lastValueFrom } from 'rxjs';
@@ -15,15 +14,7 @@ import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { CustomerUpdateRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages'; import { CustomerUpdateRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { CustomersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service'; import { CustomersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import ErrorCodes from 'src/utils/errorCodes'; import ErrorCodes from 'src/utils/errorCodes';
import jwt from 'jsonwebtoken';
import {
GetSecretValueCommand,
SecretsManagerClient,
} from '@aws-sdk/client-secrets-manager';
import getEnv from 'src/utils/getEnv';
import { logger } from 'elastic-apm-node';
// This function will accept any string, which may result in a bug.
@Injectable() @Injectable()
export class CustomersService implements OnModuleInit { export class CustomersService implements OnModuleInit {
private customerService: CustomersProtoService; private customerService: CustomersProtoService;
@@ -69,55 +60,4 @@ export class CustomersService implements OnModuleInit {
else throw err; else throw err;
} }
} }
async generateToken(
expiresIn = '30m',
data: { customerId: string; userId: string; customerName: string },
) {
const { customerId, userId: generatedById, customerName } = data;
const secretsManagerClient = new SecretsManagerClient({});
const getSecretComand = new GetSecretValueCommand({
SecretId: `${getEnv()}/${customerName}/jwt-signing-key`,
});
let private_key_pem;
await secretsManagerClient
.send(getSecretComand)
.then((res) => {
const secret = JSON.parse(res.SecretString);
private_key_pem = secret.private_key_pem;
})
.catch((err) => {
logger.error(err.stack);
throw new InternalServerErrorException(
'Error finding keys for customer',
);
});
let jwt_token;
try {
jwt_token = jwt.sign(
{
customerId,
generatedById,
},
private_key_pem,
{
algorithm: 'RS256',
expiresIn,
issuer: 'maestro',
},
);
} catch (err) {
logger.error(err.stack);
throw new InternalServerErrorException('Error generating token');
}
// const verify = jwt.verify(jwt_token, public_key_pem, {
// algorithms: ['RS256'],
// issuer: 'maestro',
// });
// const decoded = jwt.decode(jwt_token, { complete: true });
return jwt_token;
}
} }
+2
View File
@@ -5,6 +5,7 @@ import {
Inject, Inject,
Injectable, Injectable,
} from '@nestjs/common'; } from '@nestjs/common';
import { Timeout } from '@nestjs/schedule';
import CronParser, { CronExpression } from 'cron-parser'; import CronParser, { CronExpression } from 'cron-parser';
import { ClientGrpc } from '@nestjs/microservices'; import { ClientGrpc } from '@nestjs/microservices';
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist'; import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
@@ -221,6 +222,7 @@ export class InputsService {
return lastValueFrom(this.inputWriteService.InputRemove(idRequest)); return lastValueFrom(this.inputWriteService.InputRemove(idRequest));
} }
@Timeout(60000 * 10) // Timeout set for 10 minutes
async testConnection(data) { async testConnection(data) {
try { try {
const testConnectionInputResponse = const testConnectionInputResponse =
@@ -15,8 +15,6 @@ export class MixpanelController {
@Post(':id') @Post(':id')
async trackEvent(@Param('id') id, @Body() body, @User() user: RequestUser) { async trackEvent(@Param('id') id, @Body() body, @User() user: RequestUser) {
delete body.info; delete body.info;
//TODO: remove this when mixpanel track is working
return true;
const mixpanel = init(this.mixpanelToken); const mixpanel = init(this.mixpanelToken);
const separator = user.username.includes('-') ? '-' : '.'; const separator = user.username.includes('-') ? '-' : '.';
@@ -14,42 +14,44 @@ import { INote } from './dtos';
export class ProductboardController { export class ProductboardController {
@Post('notes') @Post('notes')
async sendNote(@Body() note: INote, @User() user: RequestUser) { async sendNote(@Body() note: INote, @User() user: RequestUser) {
const { title, content, tags } = note; const [request_type, urgency, connector_name = 'Não informado'] = note.tags;
const { username, customer_name } = user;
const email = username.includes('@') const { title, content } = note;
? username // const { username, customer_name } = user;
: username + `@${customer_name}.default`;
const path = process.env.PB_TOKEN_PATH; // const email = username.includes('@')
const token = await getSecretFromSecretsManager(path); // ? username
// : username + `@${customer_name}.default`;
const response = await axios // const path = process.env.PB_TOKEN_PATH;
.post( // const token = await getSecretFromSecretsManager(path);
`https://api.productboard.com/notes`,
{
title,
content,
tags,
user: {
email,
},
},
{
headers: {
Authorization: 'Bearer ' + token,
},
},
)
.catch(() => null);
if (!response) { // const response = await axios
throw new HttpException( // .post(
'Your request could not be submitted, please try again in a few moments or contact support.', // `https://api.productboard.com/notes`,
502, // {
); // title,
} // content,
// tags,
// user: {
// email,
// },
// },
// {
// headers: {
// Authorization: 'Bearer ' + token,
// },
// },
// )
// .catch(() => null);
return response.data; // if (!response) {
// throw new HttpException(
// 'Your request could not be submitted, please try again in a few moments or contact support.',
// 502,
// );
// }
// return response.data;
} }
} }
+15 -6
View File
@@ -152,9 +152,7 @@ export class UsersController {
}); });
this.logger.info('createUser', { user }); this.logger.info('createUser', { user });
this.userService.setLanguage(language); this.userService.setLanguage(language);
this.logger.info(`user: ${user}`);
this.logger.info(`body: ${body}`);
this.logger.info(`metadata: ${metadata}`);
return await this.userService.createUser(body, metadata); return await this.userService.createUser(body, metadata);
} }
@@ -173,9 +171,7 @@ export class UsersController {
access_token: user.access_token, access_token: user.access_token,
language, language,
}); });
this.logger.info(`user: ${user}`);
this.logger.info(`body: ${body}`);
this.logger.info(`metadata: ${metadata}`);
return await this.userService.batchCreateUser(body, metadata); return await this.userService.batchCreateUser(body, metadata);
} }
@@ -267,4 +263,17 @@ export class UsersController {
this.logger.info('synchronizeUser', { user }); this.logger.info('synchronizeUser', { user });
return await this.userService.synchronizeUser(id); return await this.userService.synchronizeUser(id);
} }
@Post('connector_request')
@RequireAllPermissions(PERMISSIONS_GROUPS.DADOSFERA.permissions.SYNC_USER)
@ApiOkResponse({ type: UpdateUserRes })
async sendConnectorRequest(@User() user: RequestUser, request) {
const metadata = PackTheMetadata({
customer_name: user.customer_name,
customer_id: user.customer_id,
user_id: user.user_id,
username: user.username,
});
this.userService.sendUserConnectorRequest(request, metadata);
}
} }
+13
View File
@@ -243,4 +243,17 @@ export class UsersService implements OnModuleInit {
this.usersClientService.INTERNAL_UserSynchronize({ userId }), this.usersClientService.INTERNAL_UserSynchronize({ userId }),
); );
} }
async sendUserConnectorRequest(
request: {
connector_name: string;
description: string;
title: string;
urgency: string;
},
metadata: Metadata,
) {
return await lastValueFrom(
this.usersClientService.UserConnectorRequest({ request }, metadata),
);
}
} }
-10
View File
@@ -1,10 +0,0 @@
const availableLocalEnvs: Array<typeof process.env.ENV> = ['stg', 'test'];
export default function getEnv(): typeof process.env.ENV {
if (process.env.ENV === 'local') {
if (availableLocalEnvs.includes(process.env.LOCAL_ENV))
return process.env.LOCAL_ENV;
return 'stg';
}
return process.env.ENV;
}
-20
View File
@@ -1,20 +0,0 @@
import getEnv from './getEnv';
export function getFrontendUrl(path?: string): URL {
const frontendUrl = new URL('https://app.dadosfera.ai');
if (path) frontendUrl.pathname = path;
const env = getEnv();
if (process.env.ENV === 'local') {
frontendUrl.protocol = 'http';
frontendUrl.host = 'localhost:4200';
return frontendUrl;
}
if (process.env.CLOUD_ENVIRONMENT === 'mgc')
frontendUrl.host = 'app.dadosfera.com';
if (env !== 'prd')
frontendUrl.host = frontendUrl.host.replace('app.', `app.${env}.`);
return frontendUrl;
}