mirror of
https://github.com/dadosfera/maestro.git
synced 2026-11-05 04:19:08 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb671a90d6 | ||
|
|
82290285d0 | ||
|
|
7616b1e32c | ||
|
|
adf1b3b97e | ||
|
|
6d608a0457 | ||
|
|
7764447adc | ||
|
|
576fdecf89 | ||
|
|
3de1e90fa8 | ||
|
|
5e90950660 | ||
|
|
3ed26e648f | ||
|
|
19e4daeea4 |
@@ -56,9 +56,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Helmfile
|
- name: Install Helmfile
|
||||||
run: |
|
run: |
|
||||||
wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
|
curl -fsSLO https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
|
||||||
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
|
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
|
||||||
mv helmfile /usr/local/bin/
|
sudo mv helmfile /usr/local/bin/
|
||||||
helmfile --version
|
helmfile --version
|
||||||
|
|
||||||
- name: Install Helm Diff Plugin
|
- name: Install Helm Diff Plugin
|
||||||
@@ -105,7 +105,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Helmfile
|
- name: Install Helmfile
|
||||||
run: |
|
run: |
|
||||||
wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
|
curl -fsSLO https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
|
||||||
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
|
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
|
||||||
sudo mv helmfile /usr/local/bin/
|
sudo mv helmfile /usr/local/bin/
|
||||||
helmfile --version
|
helmfile --version
|
||||||
|
|||||||
@@ -4,9 +4,18 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
nginx.ingress.kubernetes.io/whitelist-source-range: "69.49.241.121/32" # hostgator ip
|
nginx.ingress.kubernetes.io/whitelist-source-range: "69.49.241.121/32" # hostgator ip
|
||||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
|
||||||
nginx.ingress.kubernetes.io/server-snippet: |
|
nginx.ingress.kubernetes.io/server-snippet: |
|
||||||
underscores_in_headers on;
|
underscores_in_headers on;
|
||||||
ignore_invalid_headers on;
|
ignore_invalid_headers on;
|
||||||
|
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-busy-buffers-size: "64k"
|
||||||
|
{{- if .Values.maestro.restricted_ip}}
|
||||||
|
nginx.ingress.kubernetes.io/whitelist-source-range: {{ .Values.maestro.restricted_ip }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
generation: 1
|
generation: 1
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
+27
-9
@@ -42,6 +42,33 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/auth/sign-out": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "AuthController_signOut",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "dadosfera-lang",
|
||||||
|
"in": "header",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"enum": [
|
||||||
|
"pt-br",
|
||||||
|
"en-us"
|
||||||
|
],
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"Auth"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/auth/refresh-access-token": {
|
"/auth/refresh-access-token": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "AuthController_refreshAccessToken",
|
"operationId": "AuthController_refreshAccessToken",
|
||||||
@@ -3415,9 +3442,6 @@
|
|||||||
"PipelinesV2"
|
"PipelinesV2"
|
||||||
],
|
],
|
||||||
"security": [
|
"security": [
|
||||||
{
|
|
||||||
"access-token": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"access-token": []
|
"access-token": []
|
||||||
}
|
}
|
||||||
@@ -3467,9 +3491,6 @@
|
|||||||
"PipelinesV2"
|
"PipelinesV2"
|
||||||
],
|
],
|
||||||
"security": [
|
"security": [
|
||||||
{
|
|
||||||
"access-token": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"access-token": []
|
"access-token": []
|
||||||
}
|
}
|
||||||
@@ -3519,9 +3540,6 @@
|
|||||||
"PipelinesV2"
|
"PipelinesV2"
|
||||||
],
|
],
|
||||||
"security": [
|
"security": [
|
||||||
{
|
|
||||||
"access-token": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"access-token": []
|
"access-token": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
Req,
|
Req,
|
||||||
Param,
|
Param,
|
||||||
Res,
|
Res,
|
||||||
|
UnauthorizedException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
ApiHeaders,
|
ApiHeaders,
|
||||||
@@ -54,7 +55,6 @@ import jwt, { JwtPayload } 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 { Cookie } from 'express-session';
|
|
||||||
|
|
||||||
type CookiesValues = {
|
type CookiesValues = {
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
@@ -103,14 +103,13 @@ export class AuthController {
|
|||||||
const data = await this.authClient.signIn({ username, password, totp }, metadata);
|
const data = await this.authClient.signIn({ username, password, totp }, metadata);
|
||||||
|
|
||||||
if (data.tokens) {
|
if (data.tokens) {
|
||||||
this.addTokenInCookie(res, {
|
this.authClient.writeAuthSession(res, {
|
||||||
accessToken: data.tokens.accessToken,
|
accessToken: data.tokens.accessToken,
|
||||||
refreshToken: data.tokens.refreshToken,
|
refreshToken: data.tokens.refreshToken,
|
||||||
userId: data.user.id
|
userId: data.user.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return res.send(data);
|
return res.send(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('/auth - SignIn - ERROR', error);
|
this.logger.error('/auth - SignIn - ERROR', error);
|
||||||
@@ -127,27 +126,8 @@ export class AuthController {
|
|||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
this.logger.info('/auth - SignOut');
|
this.logger.info('/auth - SignOut');
|
||||||
const exp = 1000 * 60 * 3;
|
|
||||||
|
|
||||||
res.cookie('ddf-auth', '', {
|
this.authClient.cleanUpAuthSession(res);
|
||||||
domain: 'dadosfera.local',
|
|
||||||
maxAge: Date.now() - exp,
|
|
||||||
expires: new Date(),
|
|
||||||
httpOnly: true,
|
|
||||||
secure: true,
|
|
||||||
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
|
||||||
});
|
|
||||||
|
|
||||||
res.cookie('ddf-refresh-auth', '', {
|
|
||||||
domain: 'dadosfera.local',
|
|
||||||
maxAge: Date.now() - exp,
|
|
||||||
expires: new Date(),
|
|
||||||
httpOnly: true,
|
|
||||||
secure: true,
|
|
||||||
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
|
||||||
});
|
|
||||||
|
|
||||||
this.logger.info('Clean cookie sessions');
|
|
||||||
|
|
||||||
return res.send();
|
return res.send();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -176,8 +156,9 @@ export class AuthController {
|
|||||||
|
|
||||||
const data = await this.authClient.refreshAccessToken({ refreshToken, userId }, metadata);
|
const data = await this.authClient.refreshAccessToken({ refreshToken, userId }, metadata);
|
||||||
|
|
||||||
this.addTokenInCookie(res, {
|
this.authClient.writeAuthSession(res, {
|
||||||
accessToken: data.accessToken,
|
accessToken: data.accessToken,
|
||||||
|
refreshToken: data.refreshToken,
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -493,114 +474,36 @@ export class AuthController {
|
|||||||
@Get('me')
|
@Get('me')
|
||||||
async getMe(@Req() req: Request, @Res() res: Response) {
|
async getMe(@Req() req: Request, @Res() res: Response) {
|
||||||
this.logger.info('GET /auth/me ')
|
this.logger.info('GET /auth/me ')
|
||||||
// Lê cookies
|
|
||||||
|
// Get token and headers
|
||||||
const accessToken = req.cookies['ddf-auth'];
|
const accessToken = req.cookies['ddf-auth'];
|
||||||
|
const refreshToken = req.cookies['ddf-refresh-auth'];
|
||||||
const userId = req.cookies['ddf-user-id'];
|
const userId = req.cookies['ddf-user-id'];
|
||||||
|
const resourceHost = req.headers["host"]
|
||||||
|
|
||||||
|
const hasUserSession = Boolean(accessToken) && Boolean(userId);
|
||||||
|
this.logger.info('Has User Session: ' + hasUserSession);
|
||||||
|
|
||||||
|
if (!hasUserSession) {
|
||||||
|
throw new UnauthorizedException()
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.info('Has cookie: ' + Boolean(accessToken))
|
|
||||||
let payload: any;
|
|
||||||
let userInfo: any = {};
|
|
||||||
try {
|
try {
|
||||||
// Decodifica e valida o JWT de acesso
|
const userDto = await this.authClient.validateUserSession(accessToken, resourceHost);
|
||||||
const decoded: any = accessToken && jwt.decode(accessToken, { complete: true });
|
return res.status(200).json(userDto);
|
||||||
if (!decoded) throw new Error('Invalid token')
|
} catch (error) {
|
||||||
const { kid } = decoded.header;
|
|
||||||
// Busca a chave pública
|
|
||||||
const { keys } = await this.authClient.getPublicKeys();
|
|
||||||
const pemValue = keys.find((k) => k.kid === kid)?.pem;
|
|
||||||
if (!pemValue) throw new Error('Public key not found');
|
|
||||||
jwt.verify(accessToken, pemValue);
|
|
||||||
payload = decoded.payload;
|
|
||||||
userInfo = {
|
|
||||||
id: payload.user_id,
|
|
||||||
name: payload.username,
|
|
||||||
customer: {
|
|
||||||
id: payload.customer_id,
|
|
||||||
name: payload.customer_name,
|
|
||||||
tier: payload.customer_tier,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return res.status(200).json(userInfo);
|
|
||||||
} catch (err) {
|
|
||||||
this.logger.error(err.message);
|
|
||||||
const refreshToken = req.cookies['ddf-refresh-auth'];
|
|
||||||
|
|
||||||
this.logger.info('Token is invalid')
|
if (!refreshToken) {
|
||||||
this.logger.info('Has Refresh Token: '+ Boolean(refreshToken))
|
|
||||||
// Se access token inválido, tenta refresh
|
|
||||||
if (!refreshToken || !userId) {
|
|
||||||
this.logger.error('Invalid refresh token or customer name');
|
this.logger.error('Invalid refresh token or customer name');
|
||||||
return res.status(401).json({ error: 'Not authenticated' });
|
throw new UnauthorizedException("Invalid refresh token or customer name");
|
||||||
}
|
};
|
||||||
try {
|
|
||||||
// Chama refreshAccessToken
|
|
||||||
const metadata = PackTheMetadata({
|
|
||||||
});
|
|
||||||
this.logger.info('Call Refresh Token')
|
|
||||||
const data = await this.authClient.refreshAccessToken({ refreshToken, userId }, metadata);
|
|
||||||
this.logger.info('Finish Refresh Token')
|
|
||||||
// Retorna novo access token e dados mínimos
|
|
||||||
this.addTokenInCookie(res, {
|
|
||||||
accessToken: data.accessToken,
|
|
||||||
userId
|
|
||||||
});
|
|
||||||
// Decodifica novo token
|
|
||||||
const decoded: any = jwt.decode(data.accessToken, { complete: true });
|
|
||||||
const payload = decoded.payload;
|
|
||||||
userInfo = {
|
|
||||||
id: payload.user_id,
|
|
||||||
name: payload.username,
|
|
||||||
customer: {
|
|
||||||
id: payload.customer_id,
|
|
||||||
name: payload.customer_name,
|
|
||||||
tier: payload.customer_tier,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return res.status(200).json(userInfo);
|
|
||||||
} catch (refreshErr) {
|
|
||||||
this.logger.error(refreshErr)
|
|
||||||
return res.status(401).json({ error: 'Not authenticated' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private addTokenInCookie(res: Response, data: CookiesValues) {
|
const {
|
||||||
let exp = 1000 * 60 * 5; // 5 minutes
|
authSession,
|
||||||
|
user
|
||||||
if (data.accessToken) {
|
} = await this.authClient.refreshUserSession(refreshToken, userId, resourceHost);
|
||||||
const { exp: expiration } = jwt.decode(data.accessToken) as JwtPayload;
|
this.authClient.writeAuthSession(res, authSession);
|
||||||
exp = (expiration - 30) * 1000; // exp em segundos, maxAge em ms
|
return res.status(200).json(user);
|
||||||
|
|
||||||
this.logger.info('Set Cookie ddf-auth')
|
|
||||||
res.cookie('ddf-auth', data.accessToken, {
|
|
||||||
domain: '.dadosfera.ai',
|
|
||||||
maxAge: exp,
|
|
||||||
httpOnly: true,
|
|
||||||
secure: true,
|
|
||||||
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.refreshToken) {
|
|
||||||
this.logger.info('Set Cookie ddf-refresh-auth')
|
|
||||||
res.cookie('ddf-refresh-auth', data.refreshToken, {
|
|
||||||
domain: '.dadosfera.ai',
|
|
||||||
maxAge: exp,
|
|
||||||
httpOnly: true,
|
|
||||||
secure: true,
|
|
||||||
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.userId) {
|
|
||||||
this.logger.info('Set Cookie ddf-refresh-auth')
|
|
||||||
res.cookie('ddf-user-id', data.userId, {
|
|
||||||
domain: '.dadosfera.ai',
|
|
||||||
maxAge: exp,
|
|
||||||
httpOnly: true,
|
|
||||||
secure: true,
|
|
||||||
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
import { OnModuleInit, Inject, Injectable, ForbiddenException } from '@nestjs/common';
|
import {
|
||||||
|
OnModuleInit,
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
ForbiddenException,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { ClientGrpc } from '@nestjs/microservices';
|
import { ClientGrpc } from '@nestjs/microservices';
|
||||||
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
|
||||||
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
|
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
|
||||||
import { AuthProtoService as AuthServiceInterface, IdentityProviderProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
|
import {
|
||||||
|
AuthProtoService as AuthServiceInterface,
|
||||||
|
UsersProtoService,
|
||||||
|
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
|
||||||
import {
|
import {
|
||||||
AuthSnowflakeSignInRequest,
|
AuthSnowflakeSignInRequest,
|
||||||
AuthSignInRequest,
|
AuthSignInRequest,
|
||||||
@@ -21,18 +31,24 @@ import {
|
|||||||
} 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';
|
||||||
import { BulkEditResponse } from './dtos/login';
|
import { BulkEditResponse, UserDTO } from './dtos/login';
|
||||||
|
import jwt, { JwtPayload } from 'jsonwebtoken';
|
||||||
|
import { PackTheMetadata } from 'src/utils/PackTheMetadata';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
type AuthSession = {
|
||||||
|
accessToken?: string;
|
||||||
|
refreshToken?: string;
|
||||||
|
userId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthClientService implements OnModuleInit {
|
export class AuthClientService implements OnModuleInit {
|
||||||
|
|
||||||
|
|
||||||
logger: DadosferaLogger;
|
logger: DadosferaLogger;
|
||||||
|
|
||||||
|
|
||||||
private authService: AuthServiceInterface;
|
private authService: AuthServiceInterface;
|
||||||
private identityProviderService: IdentityProviderProtoService;
|
private userService: UsersProtoService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DadosferaLogger)
|
@Inject(DadosferaLogger)
|
||||||
dadosferaLogger: DadosferaLogger,
|
dadosferaLogger: DadosferaLogger,
|
||||||
@@ -46,8 +62,8 @@ export class AuthClientService implements OnModuleInit {
|
|||||||
ProtoServices.AuthProtoService,
|
ProtoServices.AuthProtoService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.identityProviderService = this.grpcClient.getService<IdentityProviderProtoService>(
|
this.userService = this.grpcClient.getService<UsersProtoService>(
|
||||||
ProtoServices.IdentityProviderProtoService,
|
ProtoServices.UsersProtoService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,11 +79,11 @@ export class AuthClientService implements OnModuleInit {
|
|||||||
return lastValueFrom(this.authService.AuthSnowflakeSignIn(input));
|
return lastValueFrom(this.authService.AuthSnowflakeSignIn(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
checkDedicatedProxy({
|
checkDedicatedProxy({ customer }: AuthSignInResponse) {
|
||||||
customer
|
|
||||||
}: AuthSignInResponse) {
|
|
||||||
const DEDICATED_PROXY = process.env.DEDICATED_PROXY || '';
|
const DEDICATED_PROXY = process.env.DEDICATED_PROXY || '';
|
||||||
this.logger.info('SignIn - Setting customer ID for dedicated proxy: ' + DEDICATED_PROXY);
|
this.logger.info(
|
||||||
|
'SignIn - Setting customer ID for dedicated proxy: ' + DEDICATED_PROXY,
|
||||||
|
);
|
||||||
this.logger.info('Customer ID: ' + customer.id);
|
this.logger.info('Customer ID: ' + customer.id);
|
||||||
|
|
||||||
if (DEDICATED_PROXY !== '' && DEDICATED_PROXY !== customer.id) {
|
if (DEDICATED_PROXY !== '' && DEDICATED_PROXY !== customer.id) {
|
||||||
@@ -75,7 +91,9 @@ export class AuthClientService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bloquear o customer de acesso o maestro publico
|
// Bloquear o customer de acesso o maestro publico
|
||||||
this.logger.info('Check if customer have network policy: ' + customer.modules);
|
this.logger.info(
|
||||||
|
'Check if customer have network policy: ' + customer.modules,
|
||||||
|
);
|
||||||
const hasNetworkPolicyModule = customer.modules.includes('network-policy');
|
const hasNetworkPolicyModule = customer.modules.includes('network-policy');
|
||||||
if (hasNetworkPolicyModule && DEDICATED_PROXY === '') {
|
if (hasNetworkPolicyModule && DEDICATED_PROXY === '') {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
@@ -94,7 +112,6 @@ export class AuthClientService implements OnModuleInit {
|
|||||||
result = await lastValueFrom(
|
result = await lastValueFrom(
|
||||||
this.authService.AuthSignIn({ username, password, totp }, metadata),
|
this.authService.AuthSignIn({ username, password, totp }, metadata),
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('SignIn - Error during sign-in');
|
this.logger.error('SignIn - Error during sign-in');
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
@@ -105,7 +122,7 @@ export class AuthClientService implements OnModuleInit {
|
|||||||
this.checkDedicatedProxy(result);
|
this.checkDedicatedProxy(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshAccessToken(
|
async refreshAccessToken(
|
||||||
@@ -115,7 +132,10 @@ export class AuthClientService implements OnModuleInit {
|
|||||||
this.logger.info('RefreshAccessToken');
|
this.logger.info('RefreshAccessToken');
|
||||||
|
|
||||||
return lastValueFrom(
|
return lastValueFrom(
|
||||||
this.authService.AuthRefreshAccessToken({ refreshToken, userId }, metadata),
|
this.authService.AuthRefreshAccessToken(
|
||||||
|
{ refreshToken, userId },
|
||||||
|
metadata,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,4 +303,190 @@ export class AuthClientService implements OnModuleInit {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async validateUserSession(accessToken: any, resourceHost: string) {
|
||||||
|
const payload = await this.validateJwtToken(accessToken);
|
||||||
|
|
||||||
|
const userDto = await this.getUserfromPayload(payload);
|
||||||
|
|
||||||
|
this.validateResourceAccess(resourceHost, userDto);
|
||||||
|
return userDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async refreshUserSession(
|
||||||
|
refreshToken: string,
|
||||||
|
userId: string,
|
||||||
|
originHeader: string,
|
||||||
|
): Promise<{
|
||||||
|
user: UserDTO;
|
||||||
|
authSession: AuthSession;
|
||||||
|
}> {
|
||||||
|
const metadata = PackTheMetadata({});
|
||||||
|
|
||||||
|
this.logger.info('Call Refresh Token');
|
||||||
|
const refreshCredentials = await this.refreshAccessToken(
|
||||||
|
{ refreshToken, userId },
|
||||||
|
metadata,
|
||||||
|
);
|
||||||
|
this.logger.info('Finish Refresh Token');
|
||||||
|
|
||||||
|
const userDto = await this.validateUserSession(
|
||||||
|
refreshCredentials.accessToken,
|
||||||
|
originHeader,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
user: userDto,
|
||||||
|
authSession: {
|
||||||
|
accessToken: refreshCredentials.accessToken,
|
||||||
|
refreshToken: refreshCredentials.refreshToken,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeAuthSession(res: Response, data: AuthSession) {
|
||||||
|
let exp = 1000 * 60 * 5; // 5 minutes
|
||||||
|
|
||||||
|
if (data.accessToken) {
|
||||||
|
const { exp: expiration } = jwt.decode(data.accessToken) as JwtPayload;
|
||||||
|
exp = (expiration - 30) * 1000; // exp em segundos, maxAge em ms
|
||||||
|
|
||||||
|
this.logger.info('Set Cookie ddf-auth');
|
||||||
|
res.cookie('ddf-auth', data.accessToken, {
|
||||||
|
domain: '.dadosfera.ai',
|
||||||
|
maxAge: exp,
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.refreshToken) {
|
||||||
|
this.logger.info('Set Cookie ddf-refresh-auth');
|
||||||
|
res.cookie('ddf-refresh-auth', data.refreshToken, {
|
||||||
|
domain: '.dadosfera.ai',
|
||||||
|
maxAge: exp,
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.userId) {
|
||||||
|
this.logger.info('Set Cookie ddf-refresh-auth');
|
||||||
|
res.cookie('ddf-user-id', data.userId, {
|
||||||
|
domain: '.dadosfera.ai',
|
||||||
|
maxAge: exp,
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public cleanUpAuthSession(res: Response) {
|
||||||
|
const exp = 1000 * 60 * 3;
|
||||||
|
|
||||||
|
res.cookie('ddf-auth', '', {
|
||||||
|
domain: 'dadosfera.ai',
|
||||||
|
maxAge: Date.now() - exp,
|
||||||
|
expires: new Date(),
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
||||||
|
});
|
||||||
|
|
||||||
|
res.cookie('ddf-refresh-auth', '', {
|
||||||
|
domain: 'dadosfera.ai',
|
||||||
|
maxAge: Date.now() - exp,
|
||||||
|
expires: new Date(),
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: 'none', // Necessário para cookies em requisições cross-site
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.info('Clean cookie sessions');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async validateJwtToken(token: string) {
|
||||||
|
const decoded: any = token && jwt.decode(token, { complete: true });
|
||||||
|
if (!decoded) throw new Error('Invalid token');
|
||||||
|
|
||||||
|
const { kid } = decoded.header;
|
||||||
|
// Busca a chave pública
|
||||||
|
const { keys } = await this.getPublicKeys();
|
||||||
|
const pemValue = keys.find((k) => k.kid === kid)?.pem;
|
||||||
|
if (!pemValue) throw new Error('Public key not found');
|
||||||
|
jwt.verify(token, pemValue);
|
||||||
|
|
||||||
|
return decoded.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getUserfromPayload(payload: JwtPayload): Promise<UserDTO> {
|
||||||
|
this.logger.info('getUser');
|
||||||
|
|
||||||
|
const metadata = PackTheMetadata({
|
||||||
|
customer_id: payload.customer_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { user } = await lastValueFrom(
|
||||||
|
this.userService.UserFindOneById({ id: payload.user_id }, metadata),
|
||||||
|
);
|
||||||
|
|
||||||
|
const userDto: UserDTO = {
|
||||||
|
id: user.id,
|
||||||
|
name: user.username,
|
||||||
|
jobTitle: user?.jobTitle || null,
|
||||||
|
department: user?.department || null,
|
||||||
|
hierarchy: user?.hierarchy || null,
|
||||||
|
customer: {
|
||||||
|
id: payload.customer_id,
|
||||||
|
name: payload.customer_name,
|
||||||
|
tier: payload.customer_tier,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return userDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateResourceAccess(host: string, user: UserDTO) {
|
||||||
|
this.logger.info(
|
||||||
|
"Validate whether the source URL is a resource belonging to the user's client",
|
||||||
|
);
|
||||||
|
this.logger.info('Host: ' + host);
|
||||||
|
this.logger.info('Customer: ' + user.customer.name);
|
||||||
|
|
||||||
|
const hostParts = host.split('.');
|
||||||
|
const domain = hostParts[0];
|
||||||
|
const isResouceStg = hostParts[1] === 'stg';
|
||||||
|
|
||||||
|
const notFoundCustomerInDomain = !domain.includes('-')
|
||||||
|
|
||||||
|
if (notFoundCustomerInDomain) {
|
||||||
|
this.logger.info(`Not found Customer Name in domain`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainParts = domain.split('-');
|
||||||
|
|
||||||
|
const customerInDomain = domainParts[domainParts.length - 1];
|
||||||
|
|
||||||
|
if (isResouceStg && process.env.ENV !== 'stg') {
|
||||||
|
this.logger.error(`Customer ${user.customer.name} cannot access ${host}`);
|
||||||
|
throw new HttpException(
|
||||||
|
`Customer ${user.customer.name} cannot access ${host}`,
|
||||||
|
HttpStatus.FORBIDDEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customerInDomain != user.customer.name) {
|
||||||
|
this.logger.error(`Customer ${user.customer.name} cannot access ${host}`);
|
||||||
|
throw new HttpException(
|
||||||
|
`Customer ${user.customer.name} cannot access ${host}`,
|
||||||
|
HttpStatus.FORBIDDEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,3 +140,16 @@ export interface BulkEditResponse {
|
|||||||
successfulUsers: string[];
|
successfulUsers: string[];
|
||||||
failedUsers: string[];
|
failedUsers: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserDTO = {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
jobTitle?: string,
|
||||||
|
department?: string,
|
||||||
|
hierarchy?: string,
|
||||||
|
customer: {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
tier: string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user