Skip to content

Commit b4d5f51

Browse files
chore: IsOrg guard api v2 (#15563)
* chore: is org guard api v2 * fixup! chore: is org guard api v2 * fixup! fixup! chore: is org guard api v2
1 parent 86fc516 commit b4d5f51

File tree

6 files changed

+176
-0
lines changed

6 files changed

+176
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ExecutionContext } from "@nestjs/common";
2+
import { createParamDecorator } from "@nestjs/common";
3+
4+
import { Team } from "@calcom/prisma/client";
5+
6+
export type GetOrgReturnType = Team;
7+
8+
export const GetOrg = createParamDecorator<
9+
keyof GetOrgReturnType | (keyof GetOrgReturnType)[],
10+
ExecutionContext
11+
>((data, ctx) => {
12+
const request = ctx.switchToHttp().getRequest();
13+
const organization = request.organization as GetOrgReturnType;
14+
15+
if (!organization) {
16+
throw new Error("GetOrg decorator : Org not found");
17+
}
18+
19+
if (Array.isArray(data)) {
20+
return data.reduce((prev, curr) => {
21+
return {
22+
...prev,
23+
[curr]: organization[curr],
24+
};
25+
}, {});
26+
}
27+
28+
if (data) {
29+
return organization[data];
30+
}
31+
32+
return organization;
33+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
2+
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
3+
import { Request } from "express";
4+
5+
import { Team } from "@calcom/prisma/client";
6+
7+
@Injectable()
8+
export class isOrgGuard implements CanActivate {
9+
constructor(private organizationsRepository: OrganizationsRepository) {}
10+
11+
async canActivate(context: ExecutionContext): Promise<boolean> {
12+
const request = context.switchToHttp().getRequest<Request & { organization: Team }>();
13+
const organizationId: string = request.params.orgId;
14+
15+
if (!organizationId) {
16+
throw new ForbiddenException("No organization id found in request params.");
17+
}
18+
19+
const org = await this.organizationsRepository.findById(Number(organizationId));
20+
21+
if (org && org.isOrganization) {
22+
request.organization = org;
23+
return true;
24+
}
25+
26+
return false;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { bootstrap } from "@/app";
2+
import { AppModule } from "@/app.module";
3+
import { PrismaModule } from "@/modules/prisma/prisma.module";
4+
import { TokensModule } from "@/modules/tokens/tokens.module";
5+
import { UsersModule } from "@/modules/users/users.module";
6+
import { INestApplication } from "@nestjs/common";
7+
import { NestExpressApplication } from "@nestjs/platform-express";
8+
import { Test } from "@nestjs/testing";
9+
import { User } from "@prisma/client";
10+
import * as request from "supertest";
11+
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
12+
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
13+
import { withApiAuth } from "test/utils/withApiAuth";
14+
15+
import { SUCCESS_STATUS } from "@calcom/platform-constants";
16+
import { ApiSuccessResponse } from "@calcom/platform-types";
17+
import { Team } from "@calcom/prisma/client";
18+
19+
describe("Organizations Team Endpoints", () => {
20+
describe("User Authentication", () => {
21+
let app: INestApplication;
22+
23+
let userRepositoryFixture: UserRepositoryFixture;
24+
let organizationsRepositoryFixture: TeamRepositoryFixture;
25+
let org: Team;
26+
27+
const userEmail = "org-teams-controller-e2e@api.com";
28+
let user: User;
29+
30+
beforeAll(async () => {
31+
const moduleRef = await withApiAuth(
32+
userEmail,
33+
Test.createTestingModule({
34+
imports: [AppModule, PrismaModule, UsersModule, TokensModule],
35+
})
36+
).compile();
37+
38+
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
39+
organizationsRepositoryFixture = new TeamRepositoryFixture(moduleRef);
40+
41+
user = await userRepositoryFixture.create({
42+
email: userEmail,
43+
username: userEmail,
44+
});
45+
46+
org = await organizationsRepositoryFixture.create({
47+
name: "Test Organization",
48+
isOrganization: true,
49+
});
50+
51+
app = moduleRef.createNestApplication();
52+
bootstrap(app as NestExpressApplication);
53+
54+
await app.init();
55+
});
56+
57+
it("should be defined", () => {
58+
expect(userRepositoryFixture).toBeDefined();
59+
expect(organizationsRepositoryFixture).toBeDefined();
60+
expect(user).toBeDefined();
61+
expect(org).toBeDefined();
62+
});
63+
64+
it("should get all the teams of the org", async () => {
65+
return request(app.getHttpServer())
66+
.get(`/v2/organizations/${org.id}/teams`)
67+
.expect(200)
68+
.then((response) => {
69+
const responseBody: ApiSuccessResponse<Team[]> = response.body;
70+
expect(responseBody.status).toEqual(SUCCESS_STATUS);
71+
expect(responseBody.data).toEqual([]);
72+
});
73+
});
74+
75+
afterAll(async () => {
76+
await userRepositoryFixture.deleteByEmail(user.email);
77+
await organizationsRepositoryFixture.delete(org.id);
78+
await app.close();
79+
});
80+
});
81+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
2+
import { GetOrg } from "@/modules/auth/decorators/get-org/get-org.decorator";
3+
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
4+
import { isOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
5+
import { Controller, UseGuards, Get, Param, ParseIntPipe } from "@nestjs/common";
6+
import { ApiTags as DocsTags } from "@nestjs/swagger";
7+
8+
import { SUCCESS_STATUS } from "@calcom/platform-constants";
9+
import { ApiResponse } from "@calcom/platform-types";
10+
import { Team } from "@calcom/prisma/client";
11+
12+
@Controller({
13+
path: "/v2/organizations/:orgId/teams",
14+
version: API_VERSIONS_VALUES,
15+
})
16+
@UseGuards(ApiAuthGuard, isOrgGuard)
17+
@DocsTags("Organizations Teams")
18+
export class OrganizationsTeamsController {
19+
@Get()
20+
async getAllTeams(
21+
@Param("orgId", ParseIntPipe) orgId: number,
22+
@GetOrg() organization: Team,
23+
@GetOrg("name") orgName: string
24+
): Promise<ApiResponse<Team[]>> {
25+
console.log(orgId, organization, orgName);
26+
return {
27+
status: SUCCESS_STATUS,
28+
data: [],
29+
};
30+
}
31+
}

apps/api/v2/src/modules/organizations/organizations.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { OrganizationsTeamsController } from "@/modules/organizations/controllers/organizations-teams.controller";
12
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
23
import { OrganizationsService } from "@/modules/organizations/services/organizations.service";
34
import { PrismaModule } from "@/modules/prisma/prisma.module";
@@ -8,5 +9,6 @@ import { Module } from "@nestjs/common";
89
imports: [PrismaModule, StripeModule],
910
providers: [OrganizationsRepository, OrganizationsService],
1011
exports: [OrganizationsService, OrganizationsRepository],
12+
controllers: [OrganizationsTeamsController],
1113
})
1214
export class OrganizationsModule {}

apps/api/v2/src/modules/organizations/organizations.repository.ts

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export class OrganizationsRepository {
1616
return this.dbRead.prisma.team.findUnique({
1717
where: {
1818
id: organizationId,
19+
isOrganization: true,
1920
},
2021
});
2122
}

0 commit comments

Comments
 (0)