feat: v2 managed organizations (#19341)
* refactor: allow non unique PlatformBilling customerId * feat: add PlatformBilling managed organizations fields * feat: ManagedOrganization table * refactor: bill overdue based on teamId * refactor: restructure organizations module * wip: organizations endpoints * Revert "wip: organizations endpoints" This reverts commit 0e9e66fc74f31da436f12930c1b6597c650ed621. * refactor: unique index for managed organizations * wip: organizations endpoints * wip: create managed organization * remove unecessary membership check because we have guard * feat: create managed org * feat: get managed org and orgs * feat: update and delete managed orgs * wip: api key logic * feat: allow variable api key length * feat: refresh managed org api key * feat: create managed org OAuth clients using api key * finish merge main * chore: bump platform libraries * tests: fix and add more tests to api-auth.strategy.e2e * refactor: dont request managed org slug and handle metadata as object * revert: billing service and repository update overdue based on sub and customer ids * refactor: v2 OAuth client permissions (#19501) * refactor: v2 permissions as string array * refactor: frontend work with permissions as string * tests: test '*' permissions * fix:managed org creator have profile & test can fetch oauth client * fix: tests * fix: OAuthClientCard on frontend * fix: tests
This commit is contained in:
1 parent
e4ab8d87c4
commit
a44bcafcf2
192 files changed
+9103
-619
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||
import { RedisService } from "@/modules/redis/redis.service";
|
||||
import { createMock } from "@golevelup/ts-jest";
|
||||
import { ExecutionContext } from "@nestjs/common";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
|
||||
import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.strategy";
|
||||
import { PlatformPlanType } from "@/modules/billing/types";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||
import { RedisService } from "@/modules/redis/redis.service";
|
||||
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
|
||||
import { Reflector } from "@nestjs/core";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrganizationsMembershipRepository } from "@/modules/organizations/repositories/organizations-membership.repository";
|
||||
import { OrganizationsMembershipRepository } from "@/modules/organizations/memberships/organizations-membership.repository";
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MembershipRoles } from "@/modules/auth/decorators/roles/membership-roles.decorator";
|
||||
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
||||
import { OrganizationsService } from "@/modules/organizations/services/organizations.service";
|
||||
import { OrganizationsService } from "@/modules/organizations/index/organizations.service";
|
||||
import { UsersService } from "@/modules/users/services/users.service";
|
||||
import { UserWithProfile } from "@/modules/users/users.repository";
|
||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||
@@ -20,10 +20,10 @@ export class OrganizationRolesGuard implements CanActivate {
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user: UserWithProfile = request.user;
|
||||
const organizationId = user ? this.usersService.getUserMainOrgId(user) : null;
|
||||
const organizationId = this.getOrganizationId(context);
|
||||
|
||||
if (!user || !organizationId) {
|
||||
throw new ForbiddenException("No organization associated with the user.");
|
||||
throw new ForbiddenException("OrganizationRolesGuard - No organization associated with the user.");
|
||||
}
|
||||
|
||||
await this.isPlatform(organizationId);
|
||||
@@ -40,13 +40,25 @@ export class OrganizationRolesGuard implements CanActivate {
|
||||
async isPlatform(organizationId: number) {
|
||||
const isPlatform = await this.organizationsService.isPlatform(organizationId);
|
||||
if (!isPlatform) {
|
||||
throw new ForbiddenException("Organization is not a platform (SHP).");
|
||||
throw new ForbiddenException("OrganizationRolesGuard - Organization is not a platform (SHP).");
|
||||
}
|
||||
}
|
||||
|
||||
getOrganizationId(context: ExecutionContext) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user: UserWithProfile = request.user;
|
||||
const authMethodOrganizationId = request.organizationId;
|
||||
if (authMethodOrganizationId) return authMethodOrganizationId;
|
||||
|
||||
const userOrganizationId = user ? this.usersService.getUserMainOrgId(user) : null;
|
||||
return userOrganizationId;
|
||||
}
|
||||
|
||||
isMembershipAccepted(accepted: boolean) {
|
||||
if (!accepted) {
|
||||
throw new ForbiddenException(`User has not accepted membership in the organization.`);
|
||||
throw new ForbiddenException(
|
||||
`OrganizationRolesGuard - User has not accepted membership in the organization.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +69,9 @@ export class OrganizationRolesGuard implements CanActivate {
|
||||
|
||||
const hasRequiredRole = allowedRoles.includes(membershipRole);
|
||||
if (!hasRequiredRole) {
|
||||
throw new ForbiddenException(`User must have one of the roles: ${allowedRoles.join(", ")}.`);
|
||||
throw new ForbiddenException(
|
||||
`OrganizationRolesGuard - User must have one of the roles: ${allowedRoles.join(", ")}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||
import { RedisService } from "@/modules/redis/redis.service";
|
||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||
import { Request } from "express";
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { ManagedOrganizationsRepository } from "@/modules/organizations/organizations/managed-organizations.repository";
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
ForbiddenException,
|
||||
NotFoundException,
|
||||
} from "@nestjs/common";
|
||||
import { Request } from "express";
|
||||
|
||||
import { Team } from "@calcom/prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class IsManagedOrgInManagerOrg implements CanActivate {
|
||||
constructor(private managedOrganizationsRepository: ManagedOrganizationsRepository) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest<Request & { managedOrganization: Team }>();
|
||||
const managedOrgId: string = request.params.managedOrganizationId;
|
||||
const managerOrgId: string = request.params.orgId;
|
||||
|
||||
if (!managerOrgId) {
|
||||
throw new ForbiddenException("No manager org id found in request params.");
|
||||
}
|
||||
|
||||
if (!managedOrgId) {
|
||||
throw new ForbiddenException("No managed org id found in request params.");
|
||||
}
|
||||
|
||||
const managedOrganization = await this.managedOrganizationsRepository.getByManagerManagedIds(
|
||||
Number(managerOrgId),
|
||||
Number(managedOrgId)
|
||||
);
|
||||
|
||||
if (!managedOrganization) {
|
||||
throw new NotFoundException(
|
||||
`Managed organization with id=${managedOrgId} within manager organization with id=${managerOrgId} not found.`
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||
import { RedisService } from "@/modules/redis/redis.service";
|
||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||
import { Request } from "express";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationsWebhooksRepository } from "@/modules/organizations/repositories/organizations-webhooks.repository";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||
import { OrganizationsWebhooksRepository } from "@/modules/organizations/webhooks/organizations-webhooks.repository";
|
||||
import { RedisService } from "@/modules/redis/redis.service";
|
||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||
import { Request } from "express";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrganizationsTeamsRepository } from "@/modules/organizations/repositories/organizations-teams.repository";
|
||||
import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/index/organizations-teams.repository";
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||
import { Request } from "express";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
|
||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||
import { Request } from "express";
|
||||
|
||||
|
||||
Reference in new issue
Block a user