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
- stg2
- prd
push_to_dockerhub:
description: "Push image to Dockerhub?"
required: true
type: boolean
default: false
jobs:
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') }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- if: github.event_name != 'workflow_dispatch'
name: Semantic Release
uses: cycjimmy/semantic-release-action@v4
uses: cycjimmy/semantic-release-action@v3
id: semantic
with:
extra_plugins: |
conventional-changelog-eslint@4.0.0
conventional-changelog-eslint
branches: |
[
'main'
@@ -81,7 +76,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Update Pip
run: |
@@ -100,14 +95,14 @@ jobs:
python3 -m pip install awsebcli --upgrade
- name: Configure AWS Region
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v1-node16
id: aws
with:
aws-region: us-east-1
- name: Login to AWS 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
env:
@@ -118,23 +113,6 @@ jobs:
docker-compose -f build.docker-compose.yml build
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
env:
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"
],
"runtimeExecutable": "npm",
"runtimeVersion": "18.10.0",
"skipFiles": [
"<node_internals>/**"
],
@@ -20,4 +19,4 @@
"console": "integratedTerminal"
}
]
}
}
+3 -3
View File
@@ -1,5 +1,5 @@
# install all dependencies
FROM node:18.10-alpine as packages
FROM node:18.3.0-alpine3.15 as packages
WORKDIR /app
COPY package.json package-lock.json ./
RUN apk add --no-cache aws-cli \
@@ -7,12 +7,12 @@ RUN apk add --no-cache aws-cli \
&& npm install
# unit test specific build
FROM node:18.10-alpine as test
FROM node:18.3.0-alpine3.15 as test
WORKDIR /app
COPY --from=packages /app/node_modules ./node_modules
COPY . ./
FROM node:18.10-alpine
FROM node:18.3.0-alpine3.15
WORKDIR /app
COPY . /app/
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
- [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)
- Create your Access Key on the "Security credentials" menu
- 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": {
"title": "Maestro - feat/generate-token",
"title": "Maestro - feature/customer-links",
"description": "This is the Maestro API",
"version": "1.0.0",
"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": {
"post": {
"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": {
"get": {
"operationId": "RolesController_searchRoles",
@@ -4984,14 +4968,7 @@
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
"description": ""
}
},
"tags": [
@@ -5010,14 +4987,7 @@
"parameters": [],
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
"description": ""
}
},
"security": [
@@ -5047,6 +5017,9 @@
"Customers"
],
"security": [
{
"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": {
"get": {
"operationId": "HealthController_check",
@@ -5144,7 +5079,7 @@
}
},
"info": {
"title": "Maestro - ci/dockerhub",
"title": "Maestro - main",
"description": "This is the Maestro API",
"version": "1.0.0",
"contact": {}
@@ -5368,15 +5303,11 @@
},
"accessToken": {
"type": "string"
},
"refreshToken": {
"type": "string"
}
},
"required": [
"permissions",
"accessToken",
"refreshToken"
"accessToken"
]
},
"CreateConnectionDto": {
-1
View File
@@ -4,7 +4,6 @@ declare global {
NODE_ENV: 'test';
ENV: 'local' | 'stg' | 'prd' | 'test';
LOCAL_ENV: 'stg' | 'prd';
CLOUD_ENVIRONMENT: 'aws' | 'gcp' | 'mgc';
DUC_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,
"license": "UNLICENSED",
"engines": {
"node": "18.10.0"
"node": "18.3.0"
},
"scripts": {
"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": {
"@aws-sdk/client-secrets-manager": "^3.112.0",
"@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/proto-loader": "^0.6.13",
"@nestjs/cli": "^9.4.2",
"@nestjs/common": "^9.4.0",
"@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.4.0",
"@nestjs/mapped-types": "^1.2.2",
"@nestjs/microservices": "^9.4.0",
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.4.0",
"@nestjs/schematics": "^9.1.0",
"@nestjs/swagger": "^6.3.0",
"@nestjs/testing": "^9.4.0",
"axios": "^1.6.2",
"@nestjs/common": "^8.4.7",
"@nestjs/config": "^1.2.1",
"@nestjs/core": "^8.4.7",
"@nestjs/mapped-types": "*",
"@nestjs/microservices": "^8.4.7",
"@nestjs/passport": "^8.2.2",
"@nestjs/platform-express": "^8.4.7",
"@nestjs/schedule": "^1.1.0",
"@nestjs/swagger": "^5.2.1",
"axios": "^0.27.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"class-validator": "^0.13.2",
"cron-parser": "^4.4.0",
"dotenv": "^14.3.2",
"elastic-apm-node": "^3.36.0",
@@ -53,7 +51,6 @@
"jsonwebtoken": "^9.0.0",
"jwk-to-pem": "^2.0.5",
"mixpanel": "^0.17.0",
"ms": "^3.0.0-canary.1",
"passport": "^0.6.0",
"passport-facebook": "^3.0.0",
"passport-forcedotcom": "^0.2.0",
@@ -70,6 +67,9 @@
"multer": "1.4.5-lts.1"
},
"devDependencies": {
"@nestjs/cli": "^8.2.8",
"@nestjs/schematics": "^8.0.11",
"@nestjs/testing": "^8.4.7",
"@types/express": "^4.17.13",
"@types/jest": "27.0.2",
"@types/jsonwebtoken": "^8.5.8",
@@ -80,8 +80,8 @@
"@types/passport-google-oauth20": "^2.0.11",
"@types/passport-oauth2": "^1.4.11",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.35.0",
"@typescript-eslint/parser": "^5.35.0",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
@@ -44,7 +44,7 @@ class NoClassAuthController {
@Get('has-all-permissions')
@RequireAllPermissions(
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE,
PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
)
async hasAllPermissions(@Body() body) {
return { body };
@@ -53,7 +53,7 @@ class NoClassAuthController {
@Get('has-some-permission')
@RequireSomePermission(
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE,
PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
)
async hasSomePermission(@Body() body) {
return { body };
@@ -62,7 +62,7 @@ class NoClassAuthController {
@Controller('class-auth-condition')
@AuthenticateCondition((req) => req.get('x-on-class') === 'ok')
@RequireSomePermission(PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE)
@RequireSomePermission(PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE)
class ClassAuthConditionController {
@Get('body')
async getBody(@Body() body) {
@@ -444,13 +444,13 @@ describe('authentication.guard', () => {
NoClassAuthTest(tokenZ, ['zendesk']);
ClassAuthConditionTest(tokenZ, ['zendesk']);
const tokenM = CreateToken([PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE]);
const tokenM = CreateToken([PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE]);
NoClassAuthTest(tokenM, ['metabase']);
ClassAuthConditionTest(tokenM, ['metabase']);
const tokenZM = CreateToken([
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE,
PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE,
]);
NoClassAuthTest(tokenZM, ['zendesk', 'metabase']);
ClassAuthConditionTest(tokenZM, ['zendesk', 'metabase']);
+1 -5
View File
@@ -79,13 +79,9 @@ export class AuthenticationGuard
request,
mustBeAuthenticated: boolean,
): RequestUser | false {
const accessToken: string = request.get('Authorization');
const accessToken = request.get('Authorization');
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.
// Fancy error avoidance isn't performed by purpose, such as avoiding to access null values.
try {
+2 -22
View File
@@ -55,16 +55,6 @@ export const PERMISSIONS_GROUPS = {
'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: {
'pt-br': 'Analisar | Visualização',
'en-us': 'Analyze | Visualization',
@@ -476,15 +466,6 @@ export const PERMISSIONS_GROUPS = {
'es-es': 'Acceso Metabase',
},
},
},
},
INTELLIGENCE: {
title: {
'pt-br': 'Analisar | Inteligência',
'en-us': 'Analyze | Intelligence',
'es-es': 'Analizar | Inteligencia',
},
permissions: {
INTELLIGENCE: {
seqid: 31,
claim: 'intelligence:open',
@@ -606,8 +587,7 @@ export const DADOSFERA_MODULES: Array<DadosferaModule> = [
name: 'Intelligence Module',
description: 'Orchest Module',
key: 'intelligence',
permissionSeqId:
PERMISSIONS_GROUPS.INTELLIGENCE.permissions.INTELLIGENCE.seqid,
permissionSeqId: PERMISSIONS_GROUPS.ANALYZE.permissions.INTELLIGENCE.seqid,
},
{
name: 'Proccessing Module',
+1 -1
View File
@@ -46,7 +46,7 @@ describe('user.decorator', () => {
const fakeUserPayload = {
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
username: 'super.admin',
permissions: [PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE].map(
permissions: [PERMISSIONS_GROUPS.ANALYZE.permissions.METABASE].map(
({ seqid }) => seqid,
),
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 { Language } from 'src/decorators/language.decorator';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
import { getFrontendUrl } from 'src/utils/getFrontendBaseUrl';
@ApiTags('Auth')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@@ -60,7 +59,7 @@ import { getFrontendUrl } from 'src/utils/getFrontendBaseUrl';
@Controller('auth')
export class AuthController {
logger: DadosferaLogger;
frontendRedirectUrl: string;
redirectUrl: string;
constructor(
@Inject(DadosferaLogger)
@@ -68,7 +67,17 @@ export class AuthController {
private authClient: AuthClientService,
) {
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')
@@ -96,7 +105,7 @@ export class AuthController {
customer_name,
language,
});
this.logger.info(`metadata: ${metadata}`);
return this.authClient.refreshAccessToken({ refreshToken }, metadata);
}
@@ -260,13 +269,7 @@ export class AuthController {
@UseGuards(AuthGuard('google-login'))
@Redirect()
async googleOauthCallback(@Req() req) {
const {
url,
email,
token,
refreshToken,
language = 'pt-br',
} = await this.callback(req);
const { url, email, token, language = 'pt-br' } = await this.callback(req);
if (url.searchParams.get('error')) {
this.logger.error('/oauth/google - ERROR');
return { url: url.href };
@@ -276,7 +279,6 @@ export class AuthController {
.oauthSignIn({
username: email,
token,
refreshToken,
})
.then(({ session }) => {
this.logger.info('/oauth/google - SUCESS');
@@ -306,79 +308,18 @@ export class AuthController {
return { url: url.href };
}
@ApiInternalOnlyEndpoint()
@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') {
async callback(req: Request) {
const { error, state } = req.query;
const { authInfo } = req;
const url = new URL(this.frontendRedirectUrl);
let email, token, refreshToken, error_title, error_description;
const language: 'pt-br' | 'en-us' = 'pt-br';
// let stateObject;
// try {
// stateObject = jwt.verify(state as string, process.env.JWT_PRIVATE_KEY);
// } catch (error) {
// console.log('error', error);
// }
// if (typeof stateObject != 'string') language = stateObject.language;
const url = new URL(this.redirectUrl);
let email, token, error_title, error_description;
let language: 'pt-br' | 'en-us' = 'pt-br';
const stateObject = jwt.verify(
state as string,
process.env.JWT_PRIVATE_KEY,
);
if (typeof stateObject != 'string') language = stateObject.language;
if (error || !authInfo) {
this.logger.error(error);
@@ -389,16 +330,11 @@ export class AuthController {
OauthErrors.INVALID_CREDENTIALS[language].error_description;
if (error) error_description += ` - [${error}]`;
} else {
const { accessToken, refreshToken: rt } = authInfo as any;
const { _json: userInfo = {} } = req.user as any;
const { accessToken } = authInfo as any;
const { _json: userInfo } = req.user as any;
email = userInfo.email;
token = accessToken;
refreshToken = rt;
if (oauth_type === 'magalu-id') {
const jwtDecoded = jwt.decode(token, { json: true });
email = jwtDecoded.email;
}
}
if (error_title) {
@@ -406,6 +342,6 @@ export class AuthController {
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 { GoogleLoginStrategy } from './passport-strategies/google-strategy';
import { getOauthSecrets } from 'src/utils/OauthSecrets';
import { MagaluIdStrategy } from './passport-strategies/magalu-id-strategy';
const client = new DucClient();
@Module({
@@ -18,7 +17,6 @@ const client = new DucClient();
AuthClientService,
DadosferaLogger,
GoogleLoginStrategy,
MagaluIdStrategy,
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
],
exports: [AuthClientService],
+3 -3
View File
@@ -17,7 +17,6 @@ import {
AuthResetPasswordRequest,
AuthVerifyResetPasswordCodeRequest,
AuthConfirmResetPasswordRequest,
AuthOauthSignInRequest,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { DucClient } from '../duc/client.config';
import { Metadata } from '@grpc/grpc-js';
@@ -163,7 +162,8 @@ export class AuthClientService implements OnModuleInit {
return lastValueFrom(this.authService.AuthGetSession({ session }));
}
async oauthSignIn(data: AuthOauthSignInRequest) {
return lastValueFrom(this.authService.AuthOauthSignIn(data));
async oauthSignIn(data: { username: string; token: string }) {
const { username, token } = data;
return lastValueFrom(this.authService.AuthOauthSignIn({ token, username }));
}
}
-2
View File
@@ -117,6 +117,4 @@ export class AuthRefreshAccessTokenRes {
permissions: string[];
@ApiProperty()
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) {
const customer = body.info.customer.toLowerCase();
const cloud_environment = process.env.CLOUD_ENVIRONMENT || 'aws';
if (process.env.ENV === 'prd' && cloud_environment === 'aws') {
if (process.env.ENV === 'prd') {
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(
'local',
'stg',
+1 -18
View File
@@ -7,7 +7,6 @@ import {
Inject,
Param,
Put,
Query,
UseFilters,
} from '@nestjs/common';
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 { CustomersService } from './customers.service';
import { CustomerDto, CustomerLinksResponse } from './dtos/customers';
import { RequestUser, User } from 'src/decorators/user.decorator';
import type { StringValue } from 'ms';
@ApiInternalOnlyController()
@ApiTags('Customers')
@@ -40,6 +37,7 @@ export class CustomersController {
}
@Get(':id/links')
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
async getCustomerLinks(@Param('id') id): Promise<CustomerLinksResponse> {
this.logger.info('getCustomerLinks', { id });
const links = await this.customersService.getLinks(id);
@@ -56,19 +54,4 @@ export class CustomersController {
this.logger.info('setCustomerLinks', { 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,
HttpException,
HttpStatus,
InternalServerErrorException,
} from '@nestjs/common';
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 { CustomersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
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()
export class CustomersService implements OnModuleInit {
private customerService: CustomersProtoService;
@@ -69,55 +60,4 @@ export class CustomersService implements OnModuleInit {
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,
Injectable,
} from '@nestjs/common';
import { Timeout } from '@nestjs/schedule';
import CronParser, { CronExpression } from 'cron-parser';
import { ClientGrpc } from '@nestjs/microservices';
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
@@ -221,6 +222,7 @@ export class InputsService {
return lastValueFrom(this.inputWriteService.InputRemove(idRequest));
}
@Timeout(60000 * 10) // Timeout set for 10 minutes
async testConnection(data) {
try {
const testConnectionInputResponse =
@@ -15,8 +15,6 @@ export class MixpanelController {
@Post(':id')
async trackEvent(@Param('id') id, @Body() body, @User() user: RequestUser) {
delete body.info;
//TODO: remove this when mixpanel track is working
return true;
const mixpanel = init(this.mixpanelToken);
const separator = user.username.includes('-') ? '-' : '.';
@@ -14,42 +14,44 @@ import { INote } from './dtos';
export class ProductboardController {
@Post('notes')
async sendNote(@Body() note: INote, @User() user: RequestUser) {
const { title, content, tags } = note;
const { username, customer_name } = user;
const [request_type, urgency, connector_name = 'Não informado'] = note.tags;
const email = username.includes('@')
? username
: username + `@${customer_name}.default`;
const { title, content } = note;
// const { username, customer_name } = user;
const path = process.env.PB_TOKEN_PATH;
const token = await getSecretFromSecretsManager(path);
// const email = username.includes('@')
// ? username
// : username + `@${customer_name}.default`;
const response = await axios
.post(
`https://api.productboard.com/notes`,
{
title,
content,
tags,
user: {
email,
},
},
{
headers: {
Authorization: 'Bearer ' + token,
},
},
)
.catch(() => null);
// const path = process.env.PB_TOKEN_PATH;
// const token = await getSecretFromSecretsManager(path);
if (!response) {
throw new HttpException(
'Your request could not be submitted, please try again in a few moments or contact support.',
502,
);
}
// const response = await axios
// .post(
// `https://api.productboard.com/notes`,
// {
// 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.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);
}
@@ -173,9 +171,7 @@ export class UsersController {
access_token: user.access_token,
language,
});
this.logger.info(`user: ${user}`);
this.logger.info(`body: ${body}`);
this.logger.info(`metadata: ${metadata}`);
return await this.userService.batchCreateUser(body, metadata);
}
@@ -267,4 +263,17 @@ export class UsersController {
this.logger.info('synchronizeUser', { user });
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 }),
);
}
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;
}