Compare commits

...
13 changed files with 270 additions and 56 deletions
+108 -3
View File
@@ -22,7 +22,7 @@
}
},
{
"name": "host",
"name": "origin",
"required": true,
"in": "header",
"schema": {
@@ -74,7 +74,7 @@
}
},
{
"name": "host",
"name": "origin",
"required": true,
"in": "header",
"schema": {
@@ -265,6 +265,14 @@
],
"type": "string"
}
},
{
"name": "origin",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
}
],
"responses": {
@@ -5465,6 +5473,57 @@
]
}
},
"/customers/{id}/mfa": {
"post": {
"operationId": "CustomersController_enableMfaEnforce",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EnforceMfa"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/customers/{id}/links": {
"get": {
"operationId": "CustomersController_getCustomerLinks",
@@ -5612,6 +5671,34 @@
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/customers/access-dashboard": {
"get": {
"operationId": "CustomersController_getAccessDashboard",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
},
@@ -5793,6 +5880,9 @@
}
},
"responses": {
"200": {
"description": ""
},
"201": {
"description": "",
"content": {
@@ -5910,6 +6000,9 @@
},
"themeEnabled": {
"type": "boolean"
},
"enforceMfa": {
"type": "boolean"
}
},
"required": [
@@ -5920,7 +6013,8 @@
"tier",
"scheduleLimit",
"links",
"themeEnabled"
"themeEnabled",
"enforceMfa"
]
},
"AuthUser": {
@@ -8589,6 +8683,17 @@
"tags"
]
},
"EnforceMfa": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
},
"required": [
"enabled"
]
},
"CustomerLink": {
"type": "object",
"properties": {
+4 -4
View File
@@ -12,7 +12,7 @@
"@aws-sdk/client-secrets-manager": "^3.414.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "3.37.0-alpha.20",
"@dadosfera/protospack-v2": "3.37.0-beta.22",
"@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0",
@@ -1397,9 +1397,9 @@
}
},
"node_modules/@dadosfera/protospack-v2": {
"version": "3.37.0-alpha.20",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.37.0-alpha.20.tgz",
"integrity": "sha512-rs8YUgnwA8dTohzSEAeilU7WAscodf26/yrsqC/lq0Dz2s3Hi09TXItnLJ5ktv5xWaBQ/eZjyc5zPjpcWL3myQ==",
"version": "3.37.0-beta.22",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.37.0-beta.22.tgz",
"integrity": "sha512-SOu+Z5qc3ms6JjtWpHFYEgYq7Q8Y2GHBewDQj5/Evv4yxpzuANw9Eecd6zzCttV47ceM75A8rnomTj9gGkoTTg==",
"license": "ISC",
"dependencies": {
"@grpc/grpc-js": "^1.9.3",
+1 -1
View File
@@ -30,7 +30,7 @@
"@aws-sdk/client-secrets-manager": "^3.414.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "3.37.0-alpha.20",
"@dadosfera/protospack-v2": "3.37.0-beta.22",
"@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0",
@@ -121,6 +121,7 @@ export class AuthenticationGuard
customer_id: accessTokenPayload.customer_id,
customer_name: accessTokenPayload.customer_name,
customer_tier: accessTokenPayload.customer_tier,
customer_modules: accessTokenPayload.customer_modules,
access_token: accessToken,
};
// TODO: for backwards compatibility. remove in the future
+7
View File
@@ -609,6 +609,13 @@ export interface DadosferaModule {
key: string;
permissionSeqId: number;
}
export const DADOSFERA_MODULES_KEYS = {
LOG_DASHBOARD: 'logs-dashboard',
ACCESS_DASHBOARD: 'access-dashboard',
DANGER_ZONE: 'danger-zone'
}
export const DADOSFERA_MODULES: Array<DadosferaModule> = [
{
name: 'Intelligence Module',
@@ -25,6 +25,14 @@ export function RequireSomePermission(
);
}
export function RequireModule(
key: string
) {
return createAuthenticatedDecorator((_, user: RequestUser) =>
user.customer_modules.some(module => module === key),
);
}
export function AuthenticateCondition(func: AuthenticationFunction) {
return createAuthenticatedDecorator(func);
}
+1
View File
@@ -11,6 +11,7 @@ export interface RequestUser {
customer_name: string;
customer_tier: string;
access_token: string;
customer_modules: string[];
}
export const User: (options?: { required?: boolean }) => ParameterDecorator =
+27 -22
View File
@@ -54,7 +54,6 @@ import { LanguageEnum } from 'src/utils/languages.enum';
import { Language } from 'src/decorators/language.decorator';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('Auth')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@UseFilters(new GrpcToHttpExceptionFilter())
@@ -63,7 +62,6 @@ export class AuthController {
logger: DadosferaLogger;
redirectUrl: string;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@@ -88,7 +86,7 @@ export class AuthController {
async signIn(
@Body() { username, password, totp }: AuthSignInReq,
@Language() language: LanguageEnum,
@Headers('origin') origin: string
@Headers('origin') origin: string,
): Promise<AuthSignInRes> {
this.logger.info('/auth - SignIn');
const frontHost = origin.replace(/^https?:\/\//, '');
@@ -103,15 +101,15 @@ export class AuthController {
async refreshAccessToken(
@Body() body: AuthRefreshAccessTokenReq,
@Language() language: LanguageEnum,
@Headers('host') host: string
@Headers('origin') origin: string,
) {
this.logger.info('/auth - RefreshAccessToken');
const { refreshToken, customerName: customer_name } = body;
const frontHost = origin.replace(/^https?:\/\//, '');
const metadata = PackTheMetadata({
customer_name,
language,
host
customer_name,
custom_host: frontHost,
});
return this.authClient.refreshAccessToken({ refreshToken }, metadata);
@@ -185,16 +183,23 @@ export class AuthController {
@ApiInternalOnlyEndpoint()
@Post('confirm-reset-password')
@HttpCode(HttpStatus.OK)
async confirmResetPassword(@Body() body: AuthConfirmResetPasswordRequest) {
async confirmResetPassword(
@Body() body: AuthConfirmResetPasswordRequest,
@Headers('origin') origin: string,
) {
this.logger.info('/auth - confirm-reset-password');
const frontHost = origin.replace(/^https?:\/\//, '');
const metadata = PackTheMetadata({ custom_host: frontHost });
const { username, code, newPassword } = body;
return this.authClient.confirmResetPassword({
username,
code,
newPassword,
});
return this.authClient.confirmResetPassword(
{
username,
code,
newPassword,
},
metadata,
);
}
@ApiInternalOnlyEndpoint()
@@ -360,18 +365,18 @@ export class AuthController {
async blockUsers(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Body() body: BulkEditRequest
@Body() body: BulkEditRequest,
) {
this.logger.info('blockUsers - Starting request');
try {
const metadata = PackTheMetadata(user);
this.logger.debug('Calling blockUsers service', {
metadata: {
access_token: metadata.get('access_token'),
language: metadata.get('language')
}
language: metadata.get('language'),
},
});
const result = await this.authClient.blockUsers(body.users, metadata);
@@ -380,7 +385,7 @@ export class AuthController {
} catch (error) {
this.logger.error('blockUsers - Error', {
error: error.message,
stack: error.stack
stack: error.stack,
});
throw error;
}
@@ -393,7 +398,7 @@ export class AuthController {
async unblockUsers(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Body() body: BulkEditRequest
@Body() body: BulkEditRequest,
) {
this.logger.info('unblockUsers');
const metadata = PackTheMetadata(user);
@@ -408,11 +413,11 @@ export class AuthController {
async resetUsers(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Body() body: BulkEditRequest
@Body() body: BulkEditRequest,
) {
this.logger.info('resetUsers');
const metadata = PackTheMetadata(user);
return this.authClient.resetUsers(body.users, metadata);
return this.authClient.resetUsers(body.users, metadata);
}
}
+32 -21
View File
@@ -113,19 +113,21 @@ export class AuthClientService implements OnModuleInit {
);
}
async confirmResetPassword({
username,
code,
newPassword,
}: AuthConfirmResetPasswordRequest) {
async confirmResetPassword(
{ username, code, newPassword }: AuthConfirmResetPasswordRequest,
metadata: Metadata,
) {
this.logger.info('confirmResetPassword');
return lastValueFrom(
this.authService.AuthConfirmResetPassword({
username,
code,
newPassword,
}),
this.authService.AuthConfirmResetPassword(
{
username,
code,
newPassword,
},
metadata,
),
);
}
@@ -170,15 +172,18 @@ export class AuthClientService implements OnModuleInit {
);
}
async blockUsers(users: string[],metadata: Metadata): Promise<BulkEditResponse> {
async blockUsers(
users: string[],
metadata: Metadata,
): Promise<BulkEditResponse> {
this.logger.info('blockUsers - Service starting');
try {
this.logger.debug('Calling BlockUser gRPC method', {
metadata: {
access_token: metadata.get('access_token'),
language: metadata.get('language')
}
language: metadata.get('language'),
},
});
const response = await lastValueFrom<BulkEditResponse>(
@@ -196,26 +201,32 @@ export class AuthClientService implements OnModuleInit {
}
}
async unblockUsers(users: string[],metadata: Metadata): Promise<BulkEditResponse> {
async unblockUsers(
users: string[],
metadata: Metadata,
): Promise<BulkEditResponse> {
this.logger.info('unblockUsers');
return await lastValueFrom(
this.authService.UnblockUser({ users }, metadata),
);
}
async resetUsers(users: string[], metadata: Metadata): Promise<BulkEditResponse> {
async resetUsers(
users: string[],
metadata: Metadata,
): Promise<BulkEditResponse> {
this.logger.info('resetUsers');
try {
this.logger.debug('Calling ResetUser gRPC method', {
metadata: {
access_token: metadata.get('access_token'),
language: metadata.get('language')
}
language: metadata.get('language'),
},
});
const response = await lastValueFrom<BulkEditResponse>(
this.authService.ResetUser({ users }, metadata)
this.authService.ResetUser({ users }, metadata),
);
this.logger.info('resetUsers - Success', { response });
@@ -223,7 +234,7 @@ export class AuthClientService implements OnModuleInit {
} catch (error) {
this.logger.error('resetUsers - Error', {
error: error.message,
stack: error.stack
stack: error.stack,
});
throw error;
}
+2
View File
@@ -87,6 +87,8 @@ export class AuthCustomer {
@ApiProperty()
themeEnabled: boolean;
@ApiProperty()
enforceMfa: boolean;
}
export class AuthSignInReq implements AuthSignInRequest {
+33 -5
View File
@@ -7,15 +7,18 @@ import {
HttpStatus,
Inject,
Param,
Post,
Put,
Query,
UseFilters,
} from '@nestjs/common';
import { ApiOkResponse, ApiProduces, ApiTags } from '@nestjs/swagger';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import { DADOSFERA_MODULES_KEYS, PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
RequireModule,
RequireSomePermission,
} from 'src/decorators/authentication.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { CustomersService } from './customers.service';
@@ -23,6 +26,7 @@ import { CustomerLinkRequest, CustomerLinksResponse } from './dtos/customers';
import { RequestUser, User } from 'src/decorators/user.decorator';
import type { StringValue } from 'ms';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { EnforceMfa } from './dtos/enforce-mfa';
@ApiTags('Customers')
@Controller('customers')
@@ -38,6 +42,15 @@ export class CustomersController {
this.logger = dadosferaLogger.logger;
}
@Post(':id/mfa')
@Authenticated()
@RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@RequireModule(DADOSFERA_MODULES_KEYS.DANGER_ZONE)
async enableMfaEnforce(@Param('id') id: string, @Body() data: EnforceMfa) {
this.logger.info('enableMfaEnforce', { id });
return await this.customersService.enableEnforceMfa(id, data.enabled);
}
@Get(':id/links')
@Authenticated()
@ApiOkResponse({ type: CustomerLinksResponse })
@@ -97,15 +110,30 @@ export class CustomersController {
@RequireAllPermissions(
PERMISSIONS_GROUPS.USERS.permissions.ADMIN,
)
@RequireModule(DADOSFERA_MODULES_KEYS.LOG_DASHBOARD)
async getCustomerMixPanelLogsDashboard(
@User() user: RequestUser,
): Promise<{ url: string }> {
this.logger.info('getLogsDashboardUrl');
): Promise<{ url: string }> {
this.logger.info('getLogsDashboardUrl');
const metadata = PackTheMetadata(user);
const result = await this.customersService.getLogsDashboardUrl(metadata);
const result = await this.customersService.getLogsDashboardUrl(metadata);
return result;
}
@Get('access-dashboard')
@Authenticated()
@RequireAllPermissions(
PERMISSIONS_GROUPS.USERS.permissions.ADMIN,
)
@RequireModule(DADOSFERA_MODULES_KEYS.ACCESS_DASHBOARD)
async getAccessDashboard(
@User() user: RequestUser,
): Promise<{ url: string }> {
this.logger.info('getAccessDashboard');
const metadata = PackTheMetadata(user);
const result = await this.customersService.getAccessDashboardUrl(user.customer_name, metadata);
return result;
}
}
@@ -5,6 +5,7 @@ import {
HttpException,
HttpStatus,
InternalServerErrorException,
ForbiddenException,
} from '@nestjs/common';
import { firstValueFrom, lastValueFrom } from 'rxjs';
@@ -33,6 +34,7 @@ import DadosferaLogger from '@dadosfera/dadosfera-logs';
// This function will accept any string, which may result in a bug.
@Injectable()
export class CustomersService implements OnModuleInit {
private customerService: CustomersProtoService;
private logger: DadosferaLogger;
private pipelineReadService: ReadService.PipelineV2ReadService;
@@ -173,4 +175,42 @@ export class CustomersService implements OnModuleInit {
return res;
}
async getAccessDashboardUrl(customerName: string, metadata: Metadata) {
/*
* TODO(Refactor): dar um jeito de exibir o dash da sbm diferente dos outros customer
* pois o signicado de department para sbm significa as instituições do usuários
*/
if (customerName !== 'sbmoffshorecom') {
throw new ForbiddenException();
}
logger.info('CustomersService - getAccessDashboardUrl');
const res = await this.getDashboardUrl('105', metadata);
logger.info('Done');
return res;
}
private async getDashboardUrl(dashboardId: string, metadata: Metadata) {
return await lastValueFrom(
this.pipelineReadService.PipelineV2GetDashboardUrl(
{
dashboard_id: dashboardId,
exp: '15m',
metabase_customer_name: 'dadosferatech',
},
metadata
)
);
}
async enableEnforceMfa(id: string, enabled: boolean) {
return await lastValueFrom(
this.customerService.CustomerUpdateEnforceMfa({
customerId: id,
enforceMfa: enabled
})
)
}
}
@@ -0,0 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
export class EnforceMfa {
@ApiProperty()
enabled: boolean
}