import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { AppLogger } from '../../../logger'; import { UserProjectRoleAccountServiceResponse } from '../models/dto'; import { ApiService } from '../communication-manager/http'; import { GetProjectPermissionRequestDto } from '../models/dto'; import { GetProjectPermissionResponseDto } from '../models/dto'; import { AuthorizationError } from '../../../exceptions/app-error/authorization-error'; import { ErrorCode } from '../../../exceptions/app-error/models/enums'; import { PermissionTransformer } from '../transformers'; import { ApiErrors } from '../enums'; import { RedisImplService } from '../../../database/redis/services'; import { RedisKeyUtil } from '../../../database/redis/utils'; import { UserPermission } from '../interfaces'; import { AuthorizationUtil } from '../utils'; import { DecodedAccessToken } from '../../interfaces'; import {AuthorizationGuardModuleConfig} from '../authorization.guard.module'; @Injectable() export class PermissionGuardService { protected TAG: string = `${this.constructor.name}`; public static SYSTEM_KEY: string = 'system'; public static ACL_KEY: string = 'acl'; static REDIS_KEY_TTL: number = 600; constructor(private readonly config: AuthorizationGuardModuleConfig, private readonly apiService: ApiService, private readonly redisService: RedisImplService) { AppLogger.log('Init', this.TAG); } public async getAccountProjectPermission(accessToken: string, accountId: string, projectShortId: string): Promise { const userProjectPermission: GetProjectPermissionResponseDto[] = await this.getAccountProjectPermissionFromRedis(accountId, projectShortId); if (userProjectPermission) { AppLogger.log(`Successfully getUserProjectPermission from Redis for account: ${accountId}`, this.TAG); return userProjectPermission; } const userProjectRoles: UserProjectRoleAccountServiceResponse[] = await this.getAccountProjectRoles(accessToken); return await this.getAccountRoles(accessToken, userProjectRoles); } public async getUserProjectPermissionName(userPermissions: GetProjectPermissionResponseDto[], projectShortId: string, accountId: string): Promise { const result: UserPermission = PermissionTransformer.getPermissionsByProject(userPermissions); this.setAccountProjectPermissionFromRedis(accountId, projectShortId, userPermissions); return result; } public async getUserSystemPermission(accessToken: string, decodedAccessToken: DecodedAccessToken): Promise { let userSystemPermissions: string[] = await this.getUserSystemPermissionFromRedis(decodedAccessToken.sub); if (userSystemPermissions) { AppLogger.log(`Successfully getUserSystemPermission from Redis for user: ${decodedAccessToken.sub}`, this.TAG); return userSystemPermissions; } const userSystemRoles: string[] = decodedAccessToken.scope; userSystemPermissions = await this.getUserSystemPermissions(accessToken, userSystemRoles); this.setUserSystemPermissionFromRedis(decodedAccessToken.sub, userSystemPermissions); return userSystemPermissions; } private async getUserSystemPermissions(accessToken: string, userSystemRoles: string[]): Promise { const userPermission: string[] = await this.getSystemPermissionsByRoles(accessToken, userSystemRoles); if (!userPermission) { AppLogger.error(`Failed to user system permissions`, this.TAG); throw new HttpException({ message: 'Failed to get user system permissions' }, HttpStatus.BAD_REQUEST); } return userPermission; } private async getAccountProjectRoles(accessToken: string): Promise { AppLogger.debug(`Try to get user project roles`, this.TAG); const userProjectRoles: UserProjectRoleAccountServiceResponse[] = await this.getAccountProjectRole(accessToken); if (!userProjectRoles) { AppLogger.error(`Failed to getUserProjectRole`, this.TAG); throw new AuthorizationError(ErrorCode.PERMISSION_GUARD_FAILED); } AppLogger.debug(`getUserProjectRole with values: ${JSON.stringify(userProjectRoles)}`, this.TAG); return userProjectRoles; } private async getAccountRoles(accessToken: string, userProjectRoles: UserProjectRoleAccountServiceResponse[]): Promise { const getProjectPermissionRequestDto: GetProjectPermissionRequestDto = AuthorizationUtil.getProjectRoleIdsByProjectShortId(userProjectRoles); AppLogger.debug(`getUserProjectRoles roleIds: ${JSON.stringify(getProjectPermissionRequestDto)}`, this.TAG); const userPermission: GetProjectPermissionResponseDto[] = await this.getProjectPermissionsByRoles(accessToken, getProjectPermissionRequestDto); if (!userPermission) { AppLogger.error(`Failed to user roles`, this.TAG); throw new HttpException({ message: 'Failed to get user permissions' }, HttpStatus.BAD_REQUEST); } return userPermission; } private async getAccountProjectRole(accessToken: string): Promise { AppLogger.crudRequest(`get`, `AccountService`, this.config.accountProjectRoleUrl, this.TAG); const response: any = await this.apiService.getAccountProjectRoleNetworkRequest(accessToken); if (!response) { AppLogger.error(ApiErrors.FAILED_GET_ACCOUNT_PROJECT_ROLES, this.TAG); return null; } AppLogger.crudSuccess(`get`, `AccountService`, this.config.accountProjectRoleUrl, this.TAG); return response; } private async getProjectPermissionsByRoles(accessToken: string, getProjectPermissionRequestDto: GetProjectPermissionRequestDto) : Promise { AppLogger.crudRequest(`get`, `Role Service`, this.config.projectPermissionUrl, this.TAG); const getProjectPermissionResponseDto: GetProjectPermissionResponseDto[] = await this.apiService.getProjectPermissionsByRoles(accessToken, getProjectPermissionRequestDto); AppLogger.crudSuccess(`get`, `Role Service`, this.config.projectPermissionUrl, this.TAG); return getProjectPermissionResponseDto; } private async getSystemPermissionsByRoles(accessToken: string, userSystemRoles: string[]): Promise { if (!userSystemRoles) { return []; } const userRoleIds: string = userSystemRoles.join(); AppLogger.crudRequest(`get`, `Role Service`, this.config.systemPermissionUrl, this.TAG); const getProjectPermissionResponseDto: string[] = await this.apiService.getSystemPermissionsByRoles(accessToken); AppLogger.crudSuccess(`get`, `Role Service`, this.config.systemPermissionUrl, this.TAG); return getProjectPermissionResponseDto; } private async getAccountProjectPermissionFromRedis(userId: string, projectShortId: string): Promise { try { AppLogger.log(`Trying to getUserProjectPermissionFromRedis for user: ${userId}, project: ${projectShortId}`, this.TAG); const value: string = await this.redisService.hGet(RedisKeyUtil.accountProjectPermission(userId, projectShortId), projectShortId); if (!value) { return null; } return JSON.parse(value); } catch (e) { AppLogger.error(`Failed to getUserProjectPermissionFromRedis for user: ${userId}, project: ${projectShortId}`, this.TAG); return null; } } private async setAccountProjectPermissionFromRedis(accountId: string, projectShortId: string, value: GetProjectPermissionResponseDto[]): Promise { try { AppLogger.log(`Trying to setUserProjectPermissionFromRedis for user: ${accountId}, project: ${projectShortId}`); const stringValue: string = JSON.stringify(value); await this.redisService.hDelete(RedisKeyUtil.accountProjectPermission(accountId, projectShortId), projectShortId); const result: boolean = await this.redisService.hmSet([RedisKeyUtil.accountProjectPermission(accountId, projectShortId), projectShortId, stringValue]); if (result) { AppLogger.debug(`Successfully setUserProjectPermissionFromRedis for user: ${accountId}, project: ${projectShortId}`); this.redisService.setExpireToKey(RedisKeyUtil.accountProjectPermission(accountId, projectShortId), PermissionGuardService.REDIS_KEY_TTL); return true; } AppLogger.error(`Failed to setUserProjectPermissionFromRedis for account: ${accountId}, project: ${projectShortId}`, this.TAG); return false; } catch (e) { AppLogger.error(`Failed to setUserProjectPermissionFromRedis for account: ${accountId}, project: ${projectShortId}`, this.TAG); return false; } } private async getUserSystemPermissionFromRedis(userId: string): Promise { try { AppLogger.log(`Trying to getUserSystemPermission for user: ${userId}`, this.TAG); const value: string = await this.redisService.hGet(RedisKeyUtil.accountProjectPermission(userId), PermissionGuardService.SYSTEM_KEY); if (!value) { return null; } return value.split(','); } catch (e) { AppLogger.error(`Failed to getUserSystemPermissionFromRedis for user: ${userId}`, this.TAG); return null; } } private async setUserSystemPermissionFromRedis(userId: string, value: string[]): Promise { try { AppLogger.log(`Trying to setUserSystemPermissionFromRedis for user: ${userId}`); const stringValue: string = value.join(); const result: boolean = await this.redisService .hmSet([RedisKeyUtil.accountProjectPermission(userId), PermissionGuardService.SYSTEM_KEY, stringValue]); if (result) { AppLogger.debug(`Successfully setUserSystemPermissionFromRedis for user: ${userId}, key: ${PermissionGuardService.SYSTEM_KEY}`); this.redisService.setExpireToKey(RedisKeyUtil.accountProjectPermission(userId), PermissionGuardService.REDIS_KEY_TTL); return true; } AppLogger.error(`Failed to setUserSystemPermissionFromRedis for user: ${userId}, key: ${PermissionGuardService.SYSTEM_KEY}`, this.TAG); return false; } catch (e) { AppLogger.error(`Failed to setUserSystemPermissionFromRedis for user: ${userId}, key: ${PermissionGuardService.SYSTEM_KEY}`, this.TAG); return false; } } }