From 21f30fc47a79d5c393ffcb8f6e77b36a324ceeec Mon Sep 17 00:00:00 2001 From: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> Date: Wed, 10 Jul 2024 18:46:04 +0300 Subject: [PATCH] chore: cache org guard and fix roles guard apiv2 (#15719) --- .../auth/guards/organizations/is-org.guard.ts | 42 ++++++++++++++++--- .../modules/auth/guards/roles/roles.guard.ts | 32 +++++++------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/apps/api/v2/src/modules/auth/guards/organizations/is-org.guard.ts b/apps/api/v2/src/modules/auth/guards/organizations/is-org.guard.ts index 44a14f5f74..ce9143d36f 100644 --- a/apps/api/v2/src/modules/auth/guards/organizations/is-org.guard.ts +++ b/apps/api/v2/src/modules/auth/guards/organizations/is-org.guard.ts @@ -1,14 +1,24 @@ import { OrganizationsRepository } from "@/modules/organizations/organizations.repository"; +import { RedisService } from "@/modules/redis/redis.service"; import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common"; import { Request } from "express"; import { Team } from "@calcom/prisma/client"; +type CachedData = { + org?: Team; + canAccess?: boolean; +}; + @Injectable() export class IsOrgGuard implements CanActivate { - constructor(private organizationsRepository: OrganizationsRepository) {} + constructor( + private organizationsRepository: OrganizationsRepository, + private readonly redisService: RedisService + ) {} async canActivate(context: ExecutionContext): Promise { + let canAccess = false; const request = context.switchToHttp().getRequest(); const organizationId: string = request.params.orgId; @@ -16,13 +26,33 @@ export class IsOrgGuard implements CanActivate { throw new ForbiddenException("No organization id found in request params."); } - const org = await this.organizationsRepository.findById(Number(organizationId)); + const REDIS_CACHE_KEY = `apiv2:org:${organizationId}:guard:isOrg`; + const cachedData = await this.redisService.redis.get(REDIS_CACHE_KEY); - if (org && org.isOrganization) { - request.organization = org; - return true; + if (cachedData) { + const { org: cachedOrg, canAccess: cachedCanAccess } = JSON.parse(cachedData) as CachedData; + if (cachedOrg?.id === Number(organizationId) && cachedCanAccess !== undefined) { + request.organization = cachedOrg; + return cachedCanAccess; + } } - return false; + const org = await this.organizationsRepository.findById(Number(organizationId)); + + if (org?.isOrganization) { + request.organization = org; + canAccess = true; + } + + if (org) { + await this.redisService.redis.set( + REDIS_CACHE_KEY, + JSON.stringify({ org: org, canAccess } satisfies CachedData), + "EX", + 300 + ); + } + + return canAccess; } } diff --git a/apps/api/v2/src/modules/auth/guards/roles/roles.guard.ts b/apps/api/v2/src/modules/auth/guards/roles/roles.guard.ts index 976c50faab..4cf1c91a58 100644 --- a/apps/api/v2/src/modules/auth/guards/roles/roles.guard.ts +++ b/apps/api/v2/src/modules/auth/guards/roles/roles.guard.ts @@ -101,23 +101,23 @@ export class RolesGuard implements CanActivate { // if the user is admin or owner of org, allow request because org > team if (`ORG_${orgMembership.role}` === "ORG_ADMIN" || `ORG_${orgMembership.role}` === "ORG_OWNER") { canAccess = true; - } + } else { + if (!teamMembership) { + this.logger.log( + `User (${user.id}) is not part of the team (${teamId}) and/or, is not an admin nor an owner of the organization (${orgId}).` + ); + throw new ForbiddenException( + "User is not part of the team and/or, is not an admin nor an owner of the organization." + ); + } - if (!teamMembership) { - this.logger.log( - `User (${user.id}) is not part of the team (${teamId}) and/or, is not an admin nor an owner of the organization (${orgId}).` - ); - throw new ForbiddenException( - "User is not part of the team and/or, is not an admin nor an owner of the organization." - ); + // if user is not admin nor an owner of org, and is part of the team, then check user team membership role + canAccess = hasMinimumRole({ + checkRole: `TEAM_${teamMembership.role}`, + minimumRole: allowedRole, + roles: TEAM_ROLES, + }); } - - // if user is not admin nor an owner of org, and is part of the team, then check user team membership role - canAccess = hasMinimumRole({ - checkRole: `TEAM_${teamMembership.role}`, - minimumRole: allowedRole, - roles: TEAM_ROLES, - }); } // if allowed role is a ORG ROLE, check org membersip role @@ -129,7 +129,7 @@ export class RolesGuard implements CanActivate { }); } } - await this.redisService.redis.set(REDIS_CACHE_KEY, String(canAccess), "EX", 3600); + await this.redisService.redis.set(REDIS_CACHE_KEY, String(canAccess), "EX", 300); return canAccess; } }