diff --git a/ServicesApi/prisma/schema.prisma b/ServicesApi/prisma/schema.prisma index f320305..613ccf0 100644 --- a/ServicesApi/prisma/schema.prisma +++ b/ServicesApi/prisma/schema.prisma @@ -3094,7 +3094,7 @@ model occupant_types { occupant_code String @default("") @db.VarChar occupant_category String @default("") @db.VarChar occupant_category_type String @default("") @db.VarChar - function_retriever String @default("") @db.VarChar + // function_retriever String @default("") @db.VarChar user_type_id Int? user_type_uu_id String? @db.VarChar occupant_is_unique Boolean @default(false) @@ -3516,7 +3516,7 @@ model staff { employee_history employee_history[] employees employees[] duties duties @relation(fields: [duties_id], references: [id], onDelete: NoAction, onUpdate: NoAction) - user_type user_types? @relation(fields: [user_type_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + user_types user_types? @relation(fields: [user_type_id], references: [id], onDelete: NoAction, onUpdate: NoAction) @@index([created_at], map: "ix_staff_created_at") @@index([cryp_uu_id], map: "ix_staff_cryp_uu_id") diff --git a/ServicesApi/src/accounts/accounts.controller.ts b/ServicesApi/src/accounts/accounts.controller.ts index 0def4c7..7d22663 100644 --- a/ServicesApi/src/accounts/accounts.controller.ts +++ b/ServicesApi/src/accounts/accounts.controller.ts @@ -14,38 +14,47 @@ import { } from '@nestjs/common'; import { AccountsService } from './accounts.service'; import { AuthControlGuard, EndpointControlGuard } from '../middleware/access-control.guard'; +import { RedisHandlers } from '../utils/auth/redisHandlers'; @Controller('accounts') export class AccountsController { - constructor(private accountsService: AccountsService) { } + constructor(private accountsService: AccountsService, private redisHandler: RedisHandlers) { } @Get('events') @HttpCode(200) - @UseGuards(AuthControlGuard, EndpointControlGuard) + @UseGuards(AuthControlGuard) async getEvents(@Query() query: any) { - const { url, func } = query; - const events = await this.accountsService.infoEvents(url, func); - return { - events, - message: "Events fetched successfully", + const { userToken } = query; + const events = await this.accountsService.infoEvents(userToken) + try { + return { events, message: "Events fetched successfully" }; + } catch (error) { + console.error('Error getting events:', error); + throw new ForbiddenException(`Error retrieving events. Please contact your system administrator.`); } } - @Post('filter') @HttpCode(200) @UseGuards(AuthControlGuard, EndpointControlGuard) async filterAccounts(@Body() query: any, @Req() req: any) { - const driveToken = req.driveToken - const redirectToService = await this.accountsService.getEvents(); - console.log('redirectToService', redirectToService); + // Get request drive token from acess control guard and retrieve related Service + const relatedService = this.accountsService.getService(req) + if (!relatedService) { throw new Error(`No service found for drive token: ${req.driveToken}`) } try { - const functionToCall = redirectToService[driveToken]; + // Get function mapper from related + if (!relatedService.mapper) { throw new Error(`Mapper in ${relatedService.constructor.name} is missing or null`) } + // Get redis select token object from redis + const selectObject = await this.redisHandler.getSelectFromRedis(req); + if (!selectObject) { throw new Error(`Select object is missing or null`) } + if (!selectObject.value.events) { throw new Error(`Events in select object is missing or null`) } + const eventKey = Object.entries(selectObject.value.events).filter((key) => key.includes(req.driveToken))[0] + if (!eventKey) { throw new Error(`No event is registered for this user ${req.driveToken}`) } + // Get function to call from related service mapper + const functionToCall = relatedService.mapper[eventKey.join(":")]; + if (!functionToCall || typeof functionToCall !== 'function') { throw new Error(`No function found for drive token: ${req.driveToken}`); } return await functionToCall(query); - } catch (error) { - console.error('Error redirecting to service:', error); - throw new ForbiddenException(`This user is not allowed to access this endpoint. Please contact your system administrator.`); - } + } catch (error) { throw new ForbiddenException(`This user is not allowed to access this endpoint. Please contact your system administrator.`) } } } diff --git a/ServicesApi/src/accounts/accounts.module.ts b/ServicesApi/src/accounts/accounts.module.ts index a2a1f17..6acefd0 100644 --- a/ServicesApi/src/accounts/accounts.module.ts +++ b/ServicesApi/src/accounts/accounts.module.ts @@ -9,6 +9,7 @@ import { EndpointControlGuard, } from '@/src/middleware/access-control.guard'; import { SuperUsersService } from './superusers/superusers.service'; +import { UrlHandler } from '../utils/auth/urlHandler'; @Module({ imports: [PrismaModule, UtilsModule], @@ -18,16 +19,11 @@ import { SuperUsersService } from './superusers/superusers.service'; AuthControlGuard, EndpointControlGuard, SuperUsersService, + UrlHandler, ], controllers: [AccountsController], }) export class AccountsModule { - constructor( - private accountsService: AccountsService, - ) { } + constructor() { } - async onModuleInit() { - const accountEvents = await this.accountsService.infoEvents(); - console.dir(accountEvents, { depth: null }); - } } diff --git a/ServicesApi/src/accounts/accounts.service.ts b/ServicesApi/src/accounts/accounts.service.ts index 6b47d01..c5db916 100644 --- a/ServicesApi/src/accounts/accounts.service.ts +++ b/ServicesApi/src/accounts/accounts.service.ts @@ -1,80 +1,33 @@ import { Injectable } from '@nestjs/common'; import { PaginationInfo } from '../utils/pagination-helper'; import { SuperUsersService } from './superusers/superusers.service'; -import crypto from 'crypto'; @Injectable() export class AccountsService { + mapper: any + constructor( private superUsersService: SuperUsersService, - ) { } - events = { - "/accounts/filter:GQKQshahQhGm8HYy4O4Tgx": [ - { - "key": "s8OnSnHoQfyfuDk7A1XRww", - "description": "Super Users Filter", - "isDefault": true, - "query": { "query": true, "page": false, "pageSize": false }, - "token": "GQKQshahQhGm8HYy4O4Tgx", - "pages": ["accounts"] - // "type": "EMP", - // "fr": "SuperUserEmployee", - } - ], - "/accounts/read:GQKQshahQhGm8HYy4O4Tgx": [ - { - "key": "s8OnSnHoQfyfuDk7A1XRww", - "description": "Super Users Read", - "isDefault": true, - "query": { "query": true, "page": false, "pageSize": false }, - "token": "GQKQshahQhGm8HYy4O4Tgx", - "pages": ["accounts"] - // "type": "EMP", - // "fr": "SuperUserEmployee", - } - ] - }; - - createSecureKeyWithoutLib(url: string) { - const subString = crypto.createHash('sha256').update(url).digest().toString('base64').substring(0, 16) - return subString.replace(/=/g, 'E').replace(/-/g, 'M').replace(/_/g, 'N').replace(/\+/g, 'P').replace(/\//g, 'Q') - } - - async infoEvents(urlRetriever: string | null = null, functionRetriever: string | null = null) { - const events = this.events; - if (urlRetriever && !functionRetriever) { - if (events[urlRetriever]) { - return [[urlRetriever, events[urlRetriever]]]; - } - return []; - } else if (urlRetriever && functionRetriever) { - if (events[urlRetriever] && events[urlRetriever][functionRetriever]) { - return [[urlRetriever, { [functionRetriever]: events[urlRetriever][functionRetriever] }]]; - } - return []; - } else if (!urlRetriever && functionRetriever) { - const filteredEvents: [string, any][] = []; - Object.entries(events).forEach(([url, urlEvents]) => { - if (urlEvents[functionRetriever]) { - filteredEvents.push([url, { [functionRetriever]: urlEvents[functionRetriever] }]); - } - }); - return filteredEvents; - } else { - return Object.entries(events); + ) { + this.mapper = { + "j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA": superUsersService, } } - async getEvents() { - return { - "/accounts/filter:GQKQshahQhGm8HYy4O4Tgx:s8OnSnHoQfyfuDk7A1XRww": (query: any) => this.supersUserFilter(query), - "/accounts/read:a5b6d9c716f409a7004a:tcc116f409a7004a": (query: any) => this.supersUserFilter(query) - }; + getService(request: any) { + const driveToken = request.driveToken + const secondPartOfDriveToken = driveToken.split(":")[1] + if (!secondPartOfDriveToken) { throw new Error('Drive token is missing or null') } + return this.mapper[secondPartOfDriveToken]; } - async supersUserFilter(query: any & { page?: number; pageSize?: number }): Promise<{ pagination: PaginationInfo; data: any[] }> { return this.superUsersService.filter(query); } + async infoEvents(userToken: string) { + const relatedMapper = this.getService(userToken) + if (!relatedMapper) { throw new Error(`No service found for user token: ${userToken}`) } + return relatedMapper.infoEvents(userToken); + } } diff --git a/ServicesApi/src/accounts/superusers/superusers.service.ts b/ServicesApi/src/accounts/superusers/superusers.service.ts index f65071d..badd6d2 100644 --- a/ServicesApi/src/accounts/superusers/superusers.service.ts +++ b/ServicesApi/src/accounts/superusers/superusers.service.ts @@ -2,14 +2,39 @@ import { PaginationHelper } from '@/src/utils/pagination-helper'; import { Injectable } from '@nestjs/common'; import { PaginationInfo } from '@/src/utils/pagination-helper'; import { PrismaService } from '@/src/prisma.service'; +import { UrlHandler } from '@/src/utils/auth/urlHandler'; @Injectable() export class SuperUsersService { + userToken: string = "j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA" constructor( private paginationHelper: PaginationHelper, private prisma: PrismaService, + private urlHandler: UrlHandler, ) { } + events = { + "e6hewIe7YqbQZHO3:j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA": [ + { + "key": "qt5P0xoeThjNT9EuWfwBgxsntHY5ydRtKFr1pgKGcgxx", + "endpoint": "/accounts/filter:POST", + "eToken": "e6hewIe7YqbQZHO3", + "token": "j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA", + "description": "Super Users Account Filter", + "isDefault": true, + "query": { "query": true, "page": false, "pageSize": false }, + "pages": [] + } + ] + }; + mapper = { + "e6hewIe7YqbQZHO3:j0adQOsJBR0xq24dxLKdDU9EQRmt4gzE05CmhA:qt5P0xoeThjNT9EuWfwBgxsntHY5ydRtKFr1pgKGcgxx": (query: any) => this.filter(query), + } + + async getEvents() { return this.urlHandler.getEvents(this.events, this.mapper) } + + async infoEvents(userToken: string) { return Object.entries(this.events).filter(([key]) => key.endsWith(userToken)) } + async filter(query: any & { page?: number; pageSize?: number }): Promise<{ pagination: PaginationInfo; data: any[] }> { console.log("supersServiceFilter query", query) const result = await this.paginationHelper.findWithPagination(query, this.prisma.account_records); diff --git a/ServicesApi/src/app.module.ts b/ServicesApi/src/app.module.ts index e83983b..e0c9bb9 100644 --- a/ServicesApi/src/app.module.ts +++ b/ServicesApi/src/app.module.ts @@ -24,9 +24,7 @@ const redisConfig = { const modulesList = [UsersModule, AccountsModule, AuthModule]; const serviceModuleList = [ PrismaModule, - RedisModule.forRoot({ - config: redisConfig, - }), + RedisModule.forRoot({ config: redisConfig }), DiscoveryModule, ]; const controllersList = [AppController]; diff --git a/ServicesApi/src/auth/login/login.service.ts b/ServicesApi/src/auth/login/login.service.ts index 78fb2af..04408c3 100644 --- a/ServicesApi/src/auth/login/login.service.ts +++ b/ServicesApi/src/auth/login/login.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { userLoginValidator } from '@/src/auth/login/dtoValidator'; -import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; -import { PasswordHandlers } from '@/src/utils/auth/login_handler'; +import { RedisHandlers } from '@/src/utils/auth/redisHandlers'; +import { PasswordHandlers } from '@/src/utils/auth/loginHandler'; import { PrismaService } from '@/src/prisma.service'; import { AuthTokenSchema } from '@/src/types/auth/token'; @@ -58,7 +58,7 @@ export class LoginService { uu_id: true, occupant_code: true, occupant_type: true, - function_retriever: true, + // function_retriever: true, }, }, build_parts: { @@ -106,7 +106,7 @@ export class LoginService { select: { uu_id: true, staff_code: true, - function_retriever: true, + // function_retriever: true, duties: { select: { uu_id: true, diff --git a/ServicesApi/src/auth/password/change/change.service.ts b/ServicesApi/src/auth/password/change/change.service.ts index b5e9f40..958c9ef 100644 --- a/ServicesApi/src/auth/password/change/change.service.ts +++ b/ServicesApi/src/auth/password/change/change.service.ts @@ -5,8 +5,8 @@ import { UnauthorizedException, } from '@nestjs/common'; import { userChangePasswordValidator } from './dtoValidator'; -import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; -import { PasswordHandlers } from '@/src/utils/auth/login_handler'; +import { RedisHandlers } from '@/src/utils/auth/redisHandlers'; +import { PasswordHandlers } from '@/src/utils/auth/loginHandler'; @Injectable() export class ChangePasswordService { @@ -14,7 +14,7 @@ export class ChangePasswordService { private readonly prisma: PrismaService, private readonly redis: RedisHandlers, private readonly passHandlers: PasswordHandlers, - ) {} + ) { } private async syncPasswordHistory( foundUser: any, @@ -84,18 +84,18 @@ export class ChangePasswordService { password: hashPassword, ...(oldestIndex === 'first' ? { - old_password_first: dto.password, - old_password_first_modified_at: new Date(), - } + old_password_first: dto.password, + old_password_first_modified_at: new Date(), + } : oldestIndex === 'second' ? { - old_password_second: dto.password, - old_password_second_modified_at: new Date(), - } + old_password_second: dto.password, + old_password_second_modified_at: new Date(), + } : { - old_password_third: dto.password, - old_password_third_modified_at: new Date(), - }), + old_password_third: dto.password, + old_password_third_modified_at: new Date(), + }), }, }); } diff --git a/ServicesApi/src/auth/password/create/create.service.ts b/ServicesApi/src/auth/password/create/create.service.ts index 6b10ab9..317d403 100644 --- a/ServicesApi/src/auth/password/create/create.service.ts +++ b/ServicesApi/src/auth/password/create/create.service.ts @@ -1,6 +1,6 @@ import { userCreatePasswordValidator } from './dtoValidator'; import { PrismaService } from '@/src/prisma.service'; -import { PasswordHandlers } from '@/src/utils/auth/login_handler'; +import { PasswordHandlers } from '@/src/utils/auth/loginHandler'; import { Injectable, BadRequestException } from '@nestjs/common'; @Injectable() @@ -8,7 +8,7 @@ export class CreatePasswordService { constructor( private readonly prisma: PrismaService, private readonly passHandlers: PasswordHandlers, - ) {} + ) { } async run(dto: userCreatePasswordValidator) { if (dto.password !== dto.rePassword) { diff --git a/ServicesApi/src/auth/password/reset/reset.service.ts b/ServicesApi/src/auth/password/reset/reset.service.ts index 3cfccf5..af2df32 100644 --- a/ServicesApi/src/auth/password/reset/reset.service.ts +++ b/ServicesApi/src/auth/password/reset/reset.service.ts @@ -1,14 +1,14 @@ import { Injectable, BadRequestException } from '@nestjs/common'; import { userResetPasswordValidator } from './dtoValidator'; import { PrismaService } from '@/src/prisma.service'; -import { PasswordHandlers } from '@/src/utils/auth/login_handler'; +import { PasswordHandlers } from '@/src/utils/auth/loginHandler'; @Injectable() export class ResetPasswordService { constructor( private readonly prisma: PrismaService, private readonly passHandlers: PasswordHandlers, - ) {} + ) { } async run(dto: userResetPasswordValidator) { const foundUser = await this.prisma.users.findFirstOrThrow({ where: { diff --git a/ServicesApi/src/auth/select/select.service.ts b/ServicesApi/src/auth/select/select.service.ts index 8bf6c56..c1b152e 100644 --- a/ServicesApi/src/auth/select/select.service.ts +++ b/ServicesApi/src/auth/select/select.service.ts @@ -5,7 +5,7 @@ import { NotAcceptableException, } from '@nestjs/common'; import { userSelectValidator } from '@/src/auth/select/dtoValidator'; -import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; +import { RedisHandlers } from '@/src/utils/auth/redisHandlers'; import { EmployeeTokenSchema, OccupantTokenSchema, @@ -13,6 +13,7 @@ import { UserType, } from '@/src/types/auth/token'; import { PrismaService } from '@/src/prisma.service'; +// No need to import Prisma client types directly @Injectable() export class SelectService { @@ -47,9 +48,32 @@ export class SelectService { }, }); const staff = await this.prisma.staff.findFirstOrThrow({ - where: { id: employee.staff_id }, - omit: { - id: true, + where: { uu_id: employee.staff_uu_id }, + select: { + uu_id: true, + staff_code: true, + user_type_id: true, + duties_id: true, + duties_uu_id: true, + created_credentials_token: true, + updated_credentials_token: true, + confirmed_credentials_token: true, + is_confirmed: true, + deleted: true, + active: true, + is_notification_send: true, + is_email_send: true, + expiry_starts: true, + expiry_ends: true, + created_at: true, + updated_at: true, + ref_int: true, + user_types: { + select: { + token: true, + type_token: true + } + } }, }); const duties = await this.prisma.duties.findFirstOrThrow({ @@ -77,7 +101,17 @@ export class SelectService { }, }); + const staffUserType = staff.user_type_id ? + await this.prisma.user_types.findFirst({ + where: { id: staff.user_type_id }, + select: { + token: true, + type_token: true + } + }) : null; + const employeeToken = EmployeeTokenSchema.parse({ + uuid: dto.uuid, company: company, department: department, duty: duty, @@ -85,6 +119,7 @@ export class SelectService { staff: staff, menu: null, pages: null, + events: null, selection: await this.prisma.employees.findFirstOrThrow({ where: { uu_id: dto.uuid }, select: { @@ -93,7 +128,12 @@ export class SelectService { select: { uu_id: true, staff_code: true, - function_retriever: true, + user_types: { + select: { + uu_id: true, + token: true, + }, + }, duties: { select: { uu_id: true, @@ -122,7 +162,8 @@ export class SelectService { }, }, }), - functionsRetriever: staff.function_retriever, + typeToken: staffUserType?.type_token, + functionsRetriever: staffUserType?.token, kind: UserType.employee, }); @@ -138,57 +179,73 @@ export class SelectService { token: tokenSelect, }; } else if (userType === 'occupant') { - const livingSpace = await this.prisma.build_living_space.findFirstOrThrow( - { - where: { uu_id: dto.uuid }, - omit: { - id: true, - person_id: true, - build_parts_id: true, - occupant_type_id: true, - ref_id: true, - replication_id: true, - cryp_uu_id: true, - }, - }, - ); - const occupantType = await this.prisma.occupant_types.findFirstOrThrow({ - where: { uu_id: livingSpace.occupant_type_uu_id }, - omit: { - id: true, - cryp_uu_id: true, - ref_id: true, - replication_id: true, - }, + const livingSpace = await this.prisma.build_living_space.findFirstOrThrow({ + where: { uu_id: dto.uuid }, + select: { + uu_id: true, + build_parts_uu_id: true, + occupant_type_uu_id: true + } }); + + const occupantType = await this.prisma.occupant_types.findFirstOrThrow({ + where: { uu_id: livingSpace.occupant_type_uu_id } + }); + + const userTypeInfo = occupantType.user_type_uu_id ? + await this.prisma.user_types.findFirst({ + where: { uu_id: occupantType.user_type_uu_id }, + select: { + uu_id: true, + type: true, + description: true, + type_token: true, + token: true + } + }) : null; + const part = await this.prisma.build_parts.findFirstOrThrow({ where: { uu_id: livingSpace.build_parts_uu_id }, - omit: { - id: true, - cryp_uu_id: true, - ref_id: true, - replication_id: true, - }, + select: { + uu_id: true, + part_code: true, + part_no: true, + part_level: true, + human_livable: true, + build_uu_id: true, + api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown: { + select: { + uu_id: true, + enum_class: true, + value: true + } + } + } }); + const build = await this.prisma.build.findFirstOrThrow({ where: { uu_id: part.build_uu_id }, - omit: { - id: true, - cryp_uu_id: true, - ref_id: true, - replication_id: true, - }, + select: { + uu_id: true, + build_name: true + } }); + const company = await this.prisma.companies.findFirstOrThrow({ where: { uu_id: accessObject.value.users.related_company }, - omit: { - id: true, - cryp_uu_id: true, - ref_id: true, - replication_id: true, - }, + select: { + uu_id: true, + is_confirmed: true, + deleted: true, + active: true, + created_at: true, + updated_at: true, + ref_int: true + } }); + const occupantToken = OccupantTokenSchema.parse({ + uuid: dto.uuid, livingSpace: livingSpace, occupant: occupantType, build: build, @@ -196,54 +253,44 @@ export class SelectService { company: company, menu: null, pages: null, - selection: await this.prisma.build_living_space.findFirstOrThrow({ - where: { uu_id: dto.uuid }, - select: { - uu_id: true, - occupant_types: { - select: { - uu_id: true, - occupant_code: true, - occupant_type: true, - function_retriever: true, - }, - }, - build_parts: { - select: { - uu_id: true, - part_code: true, - part_no: true, - part_level: true, - human_livable: true, - api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown: { - select: { - uu_id: true, - enum_class: true, - value: true, - }, - }, - build: { - select: { - uu_id: true, - build_name: true, - }, - }, - }, - }, + events: null, + selection: { + occupant_types: { + uu_id: occupantType.uu_id, + occupant_code: occupantType.occupant_code, + occupant_type: occupantType.occupant_type }, - }), - functionsRetriever: occupantType.function_retriever, - kind: UserType.occupant, + build_parts: { + uu_id: part.uu_id, + part_code: part.part_code, + part_no: part.part_no, + part_level: part.part_level, + human_livable: part.human_livable, + api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown: { + uu_id: part.api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown.uu_id, + enum_class: part.api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown.enum_class, + value: part.api_enum_dropdown_build_parts_part_type_idToapi_enum_dropdown.value + }, + build: { + uu_id: build.uu_id, + build_name: build.build_name + } + } + }, + typeToken: userTypeInfo?.type_token, + functionsRetriever: userTypeInfo?.token, + kind: UserType.occupant }); + const tokenSelect = await this.redis.setSelectToRedis( accessToken, occupantToken, accessObject.value.users.uu_id, - dto.uuid, + dto.uuid ); return { message: 'Select successful', - token: tokenSelect, + token: tokenSelect }; } else { throw new NotAcceptableException('Invalid user type'); diff --git a/ServicesApi/src/middleware/access-control.guard.ts b/ServicesApi/src/middleware/access-control.guard.ts index 61285f5..edba33b 100644 --- a/ServicesApi/src/middleware/access-control.guard.ts +++ b/ServicesApi/src/middleware/access-control.guard.ts @@ -4,7 +4,8 @@ import { Injectable, ForbiddenException, } from '@nestjs/common'; -import { RedisHandlers } from '@/src/utils/auth/redis_handlers'; +import { RedisHandlers } from '@/src/utils/auth/redisHandlers'; +import { UrlHandler } from '@/src/utils/auth/urlHandler'; @Injectable() export class AuthControlGuard implements CanActivate { @@ -13,25 +14,27 @@ export class AuthControlGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const req = context.switchToHttp().getRequest(); const accessToken = this.cacheService.mergeLoginKey(req); - console.log('AuthControlGuard', accessToken); + // console.log('AuthControlGuard', accessToken); return true; } } @Injectable() export class EndpointControlGuard implements CanActivate { - constructor(private cacheService: RedisHandlers) { } + constructor( + private cacheService: RedisHandlers, + private urlHandler: UrlHandler, + ) { } async canActivate(context: ExecutionContext): Promise { const req = context.switchToHttp().getRequest(); - // const selectToken = this.cacheService.mergeSelectKey(req); const method = req.method; const path = req.route?.path; - console.log('EndpointControlGuard', method, 'path', path); - // const accessObject = await this.cacheService.getSelectFromRedis(req); - // console.log('EndpointControlGuard', accessObject); - req.driveToken = "c5b6d9c7-9115-4825-bcc1-16f409a7004a" - // console.log('EndpointControlGuard', req.driveToken); + const keyUrl = `${path}:${method.toUpperCase()}`; + const driveToken = await this.urlHandler.getSecureUrlToken(keyUrl); + const accessObject = await this.cacheService.getSelectFromRedis(req); + req.driveToken = `${driveToken}:${accessObject?.value.functionsRetriever}`; + console.log('EndpointControlGuard driveToken: ', driveToken); return true; } } diff --git a/ServicesApi/src/navigator/events/events.service.spec.ts b/ServicesApi/src/navigator/events/events.service.spec.ts new file mode 100644 index 0000000..f26bdfe --- /dev/null +++ b/ServicesApi/src/navigator/events/events.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { EventsService } from './events.service'; + +describe('EventsService', () => { + let service: EventsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [EventsService], + }).compile(); + + service = module.get(EventsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/ServicesApi/src/navigator/events/events.service.ts b/ServicesApi/src/navigator/events/events.service.ts new file mode 100644 index 0000000..7ffeae9 --- /dev/null +++ b/ServicesApi/src/navigator/events/events.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class EventsService {} diff --git a/ServicesApi/src/navigator/pages/pages.service.spec.ts b/ServicesApi/src/navigator/pages/pages.service.spec.ts new file mode 100644 index 0000000..6cf9c50 --- /dev/null +++ b/ServicesApi/src/navigator/pages/pages.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PagesService } from './pages.service'; + +describe('PagesService', () => { + let service: PagesService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [PagesService], + }).compile(); + + service = module.get(PagesService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/ServicesApi/src/navigator/pages/pages.service.ts b/ServicesApi/src/navigator/pages/pages.service.ts new file mode 100644 index 0000000..1a55aae --- /dev/null +++ b/ServicesApi/src/navigator/pages/pages.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class PagesService {} diff --git a/ServicesApi/src/navigator/services/services.service.spec.ts b/ServicesApi/src/navigator/services/services.service.spec.ts new file mode 100644 index 0000000..29736b8 --- /dev/null +++ b/ServicesApi/src/navigator/services/services.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ServicesService } from './services.service'; + +describe('ServicesService', () => { + let service: ServicesService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ServicesService], + }).compile(); + + service = module.get(ServicesService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/ServicesApi/src/navigator/services/services.service.ts b/ServicesApi/src/navigator/services/services.service.ts new file mode 100644 index 0000000..c432676 --- /dev/null +++ b/ServicesApi/src/navigator/services/services.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ServicesService {} diff --git a/ServicesApi/src/types/auth/token.ts b/ServicesApi/src/types/auth/token.ts index e74e9a8..69c4938 100644 --- a/ServicesApi/src/types/auth/token.ts +++ b/ServicesApi/src/types/auth/token.ts @@ -1,4 +1,4 @@ -import { z } from 'zod'; +import { uuid, z } from 'zod'; // ENUM export const UserType = { @@ -101,6 +101,7 @@ export const AuthTokenSchema = z.object({ export type AuthToken = z.infer; export const EmployeeTokenSchema = z.object({ + uuid: z.string(), company: z.object({ // id: z.number(), uu_id: z.string(), @@ -218,7 +219,7 @@ export const EmployeeTokenSchema = z.object({ staff_code: z.string(), // duties_id: z.number(), duties_uu_id: z.string(), - function_retriever: z.string().nullable(), + // function_retriever: z.string().nullable(), // ref_id: z.string().nullable(), // replication_id: z.number(), // cryp_uu_id: z.string().nullable(), @@ -239,13 +240,16 @@ export const EmployeeTokenSchema = z.object({ menu: z.array(z.object({})).nullable(), pages: z.array(z.string()).nullable(), + events: z.array(z.string()).nullable(), selection: z.record(z.string(), z.unknown()).nullable(), + typeToken: z.string(), functionsRetriever: z.string(), kind: z.literal(UserType.employee), }); export const OccupantTokenSchema = z.object({ + uuid: z.string(), livingSpace: z.object({}), occupant: z.object({}), build: z.object({}), @@ -254,8 +258,10 @@ export const OccupantTokenSchema = z.object({ menu: z.array(z.object({})).nullable(), pages: z.array(z.string()).nullable(), + events: z.array(z.string()).nullable(), selection: z.record(z.string(), z.unknown()).nullable(), + typeToken: z.string(), functionsRetriever: z.string(), kind: z.literal(UserType.occupant), }); diff --git a/ServicesApi/src/utils/auth/login_handler.ts b/ServicesApi/src/utils/auth/loginHandler.ts similarity index 100% rename from ServicesApi/src/utils/auth/login_handler.ts rename to ServicesApi/src/utils/auth/loginHandler.ts diff --git a/ServicesApi/src/utils/auth/redis_handlers.ts b/ServicesApi/src/utils/auth/redisHandlers.ts similarity index 98% rename from ServicesApi/src/utils/auth/redis_handlers.ts rename to ServicesApi/src/utils/auth/redisHandlers.ts index 6662778..4eef208 100644 --- a/ServicesApi/src/utils/auth/redis_handlers.ts +++ b/ServicesApi/src/utils/auth/redisHandlers.ts @@ -5,7 +5,7 @@ import { AuthTokenSchema, } from '@/src/types/auth/token'; import { CacheService } from '@/src/cache.service'; -import { PasswordHandlers } from './login_handler'; +import { PasswordHandlers } from './loginHandler'; import { Injectable, ForbiddenException } from '@nestjs/common'; interface LoginFromRedis { @@ -152,7 +152,7 @@ export class RedisHandlers { for (const key of keys) { const value = await this.cacheService.get(key); if (value) { - return { key, value }; + return { key: value.key, value: value.value }; } } throw new ForbiddenException( diff --git a/ServicesApi/src/utils/auth/urlHandler.ts b/ServicesApi/src/utils/auth/urlHandler.ts new file mode 100644 index 0000000..51b16cc --- /dev/null +++ b/ServicesApi/src/utils/auth/urlHandler.ts @@ -0,0 +1,68 @@ +import { Injectable } from "@nestjs/common"; +import { Events, Mapper } from "@/src/utils/types/url"; +import { createHash } from 'crypto'; + +@Injectable() +export class UrlHandler { + private createSecureKeyWithoutLib(url: string): string { + const subString = createHash('sha256').update(url).digest().toString('base64').substring(0, 16) + return subString.replace(/=/g, 'E').replace(/-/g, 'M').replace(/_/g, 'N').replace(/\+/g, 'P').replace(/\//g, 'Q') + } + + async getSecureUrlToken(url: string): Promise { + return this.createSecureKeyWithoutLib(url); + } + + + async getEvents(events: Events, mapper: Mapper) { + for (const keyUrl of Object.keys(mapper)) { + const splittedMapper = keyUrl.split(':') + const eToken = splittedMapper[0] + const token = splittedMapper[1] + const key = splittedMapper[2] + const eventKey = `${eToken}:${token}` + + if (Object.keys(events).includes(eventKey)) { + // Check if the event contains an item with the matching key + const eventArray = events[eventKey] + const foundEvent = eventArray.find(item => item.key === key) + + if (!foundEvent) { + throw new Error(`Event key ${key} not found in event ${eventKey}`) + } + } else { + throw new Error(`Event ${eventKey} not found in events`) + } + } + return mapper; + } + + async infoEvents(events: Events, urlRetriever: string | null = null, functionRetriever: string | null = null) { + if (urlRetriever && !functionRetriever) { + console.log("urlRetriever", urlRetriever) + if (events[urlRetriever]) { + return [[urlRetriever, events[urlRetriever]]]; + } + return []; + } else if (urlRetriever && functionRetriever) { + if (events[urlRetriever]) { + const eventItem = events[urlRetriever].find(item => item.key === functionRetriever); + if (eventItem) { + return [[urlRetriever, { [functionRetriever]: eventItem }]]; + } + } + return []; + } else if (!urlRetriever && functionRetriever) { + const filteredEvents: [string, any][] = []; + Object.entries(events).forEach(([url, eventArray]) => { + const eventItem = eventArray.find(item => item.key === functionRetriever); + if (eventItem) { + filteredEvents.push([url, { [functionRetriever]: eventItem }]); + } + }); + return filteredEvents; + } else { + return Object.entries(events); + } + } +} \ No newline at end of file diff --git a/ServicesApi/src/utils/types/url.ts b/ServicesApi/src/utils/types/url.ts new file mode 100644 index 0000000..5ddd1b5 --- /dev/null +++ b/ServicesApi/src/utils/types/url.ts @@ -0,0 +1,16 @@ +export interface Events { + [key: string]: Array<{ + endpoint: string; + eToken: string; + token: string; + key: string; + description: string; + isDefault: boolean; + query: Record; + pages: string[]; + }>; +} + +export interface Mapper { + [key: string]: (query: any) => any; +} diff --git a/ServicesApi/src/utils/utils.module.ts b/ServicesApi/src/utils/utils.module.ts index 97f0ab1..50300e4 100644 --- a/ServicesApi/src/utils/utils.module.ts +++ b/ServicesApi/src/utils/utils.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { PaginationHelper } from './pagination-helper'; import { PrismaService } from '@/src/prisma.service'; -import { RedisHandlers } from './auth/redis_handlers'; -import { PasswordHandlers } from './auth/login_handler'; +import { RedisHandlers } from './auth/redisHandlers'; +import { PasswordHandlers } from './auth/loginHandler'; import { CacheService } from '@/src/cache.service'; @Module({ @@ -15,4 +15,4 @@ import { CacheService } from '@/src/cache.service'; ], exports: [PaginationHelper, RedisHandlers, PasswordHandlers, CacheService], }) -export class UtilsModule {} +export class UtilsModule { }