321 lines
8.4 KiB
TypeScript
321 lines
8.4 KiB
TypeScript
import { describe, expect, test, vi, beforeEach } from "vitest";
|
|
|
|
import { CredentialRepository } from "@calcom/features/credentials/repositories/CredentialRepository";
|
|
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
|
import { HttpError } from "@calcom/lib/http-error";
|
|
import { prisma } from "@calcom/prisma";
|
|
|
|
import { CredentialAccessService } from "./CredentialAccessService";
|
|
|
|
vi.mock("@calcom/prisma", () => {
|
|
return {
|
|
prisma: {
|
|
user: {
|
|
findUnique: vi.fn(),
|
|
},
|
|
},
|
|
};
|
|
});
|
|
|
|
vi.mock("@calcom/features/credentials/repositories/CredentialRepository", () => {
|
|
return {
|
|
CredentialRepository: {
|
|
findFirstByIdWithKeyAndUser: vi.fn(),
|
|
},
|
|
};
|
|
});
|
|
|
|
vi.mock("@calcom/features/users/repositories/UserRepository", () => {
|
|
return {
|
|
UserRepository: vi.fn().mockImplementation(() => ({
|
|
getUserOrganizationAndTeams: vi.fn(),
|
|
})),
|
|
};
|
|
});
|
|
|
|
describe("CredentialAccessService", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
test("should allow access when credential belongs to logged-in user", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = 200;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue({
|
|
id: credentialId,
|
|
userId: loggedInUserId,
|
|
teamId: null,
|
|
type: "zoom_video",
|
|
appId: "zoom",
|
|
key: {},
|
|
invalid: false,
|
|
user: { email: "user@example.com" },
|
|
delegatedTo: null,
|
|
delegatedToId: null,
|
|
delegationCredentialId: null,
|
|
} as any);
|
|
|
|
const service = new CredentialAccessService();
|
|
await expect(
|
|
service.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
).resolves.not.toThrow();
|
|
});
|
|
|
|
test("should allow access when credential belongs to booking owner", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = 200;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue({
|
|
id: credentialId,
|
|
userId: bookingOwnerId,
|
|
teamId: null,
|
|
type: "zoom_video",
|
|
appId: "zoom",
|
|
key: {},
|
|
invalid: false,
|
|
user: { email: "owner@example.com" },
|
|
delegatedTo: null,
|
|
delegatedToId: null,
|
|
delegationCredentialId: null,
|
|
} as any);
|
|
|
|
const service = new CredentialAccessService();
|
|
await expect(
|
|
service.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
).resolves.not.toThrow();
|
|
});
|
|
|
|
test("should allow access when credential belongs to team the logged-in user belongs to", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = 200;
|
|
const teamId = 50;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue({
|
|
id: credentialId,
|
|
userId: null,
|
|
teamId: teamId,
|
|
type: "zoom_video",
|
|
appId: "zoom",
|
|
key: {},
|
|
invalid: false,
|
|
user: null,
|
|
delegatedTo: null,
|
|
delegatedToId: null,
|
|
delegationCredentialId: null,
|
|
} as any);
|
|
|
|
const mockUserRepo = {
|
|
getUserOrganizationAndTeams: vi.fn().mockResolvedValue({
|
|
organizationId: null,
|
|
teams: [{ teamId: teamId }],
|
|
}),
|
|
};
|
|
|
|
vi.mocked(UserRepository).mockImplementation(() => mockUserRepo as any);
|
|
|
|
const service = new CredentialAccessService();
|
|
await expect(
|
|
service.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
).resolves.not.toThrow();
|
|
});
|
|
|
|
test("should allow access when credential belongs to team the booking owner belongs to", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = 200;
|
|
const teamId = 50;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue({
|
|
id: credentialId,
|
|
userId: null,
|
|
teamId: teamId,
|
|
type: "zoom_video",
|
|
appId: "zoom",
|
|
key: {},
|
|
invalid: false,
|
|
user: null,
|
|
delegatedTo: null,
|
|
delegatedToId: null,
|
|
delegationCredentialId: null,
|
|
} as any);
|
|
|
|
const mockUserRepo = {
|
|
getUserOrganizationAndTeams: vi
|
|
.fn()
|
|
.mockResolvedValueOnce({
|
|
organizationId: null,
|
|
teams: [],
|
|
})
|
|
.mockResolvedValueOnce({
|
|
organizationId: null,
|
|
teams: [{ teamId: teamId }],
|
|
}),
|
|
};
|
|
|
|
vi.mocked(UserRepository).mockImplementation(() => mockUserRepo as any);
|
|
|
|
const service = new CredentialAccessService();
|
|
await expect(
|
|
service.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
).resolves.not.toThrow();
|
|
});
|
|
|
|
test("should allow access when credential belongs to organization the logged-in user belongs to", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = 200;
|
|
const orgId = 50;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue({
|
|
id: credentialId,
|
|
userId: null,
|
|
teamId: orgId,
|
|
type: "zoom_video",
|
|
appId: "zoom",
|
|
key: {},
|
|
invalid: false,
|
|
user: null,
|
|
delegatedTo: null,
|
|
delegatedToId: null,
|
|
delegationCredentialId: null,
|
|
} as any);
|
|
|
|
const mockUserRepo = {
|
|
getUserOrganizationAndTeams: vi.fn().mockResolvedValue({
|
|
organizationId: orgId,
|
|
teams: [],
|
|
}),
|
|
};
|
|
|
|
vi.mocked(UserRepository).mockImplementation(() => mockUserRepo as any);
|
|
|
|
const service = new CredentialAccessService();
|
|
await expect(
|
|
service.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
).resolves.not.toThrow();
|
|
});
|
|
|
|
test("should throw NOT_FOUND when credential does not exist", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = 200;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue(null);
|
|
|
|
const service = new CredentialAccessService();
|
|
const error = await service
|
|
.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
.catch((e) => e);
|
|
|
|
expect(error).toBeInstanceOf(HttpError);
|
|
expect(error.statusCode).toBe(404);
|
|
expect(error.message).toBe("Credential not found");
|
|
});
|
|
|
|
test("should throw FORBIDDEN when credential is not accessible", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = 200;
|
|
const otherUserId = 300;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue({
|
|
id: credentialId,
|
|
userId: otherUserId,
|
|
teamId: null,
|
|
type: "zoom_video",
|
|
appId: "zoom",
|
|
key: {},
|
|
invalid: false,
|
|
user: { email: "other@example.com" },
|
|
delegatedTo: null,
|
|
delegatedToId: null,
|
|
delegationCredentialId: null,
|
|
} as any);
|
|
|
|
const mockUserRepo = {
|
|
getUserOrganizationAndTeams: vi
|
|
.fn()
|
|
.mockResolvedValueOnce({
|
|
organizationId: null,
|
|
teams: [],
|
|
})
|
|
.mockResolvedValueOnce({
|
|
organizationId: null,
|
|
teams: [],
|
|
}),
|
|
};
|
|
|
|
vi.mocked(UserRepository).mockImplementation(() => mockUserRepo as any);
|
|
|
|
const service = new CredentialAccessService();
|
|
const error = await service
|
|
.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
.catch((e) => e);
|
|
|
|
expect(error).toBeInstanceOf(HttpError);
|
|
expect(error.statusCode).toBe(403);
|
|
expect(error.message).toBe("You do not have access to this credential");
|
|
});
|
|
|
|
test("should handle null bookingOwnerId", async () => {
|
|
const credentialId = 1;
|
|
const loggedInUserId = 100;
|
|
const bookingOwnerId = null;
|
|
|
|
vi.mocked(CredentialRepository.findFirstByIdWithKeyAndUser).mockResolvedValue({
|
|
id: credentialId,
|
|
userId: loggedInUserId,
|
|
teamId: null,
|
|
type: "zoom_video",
|
|
appId: "zoom",
|
|
key: {},
|
|
invalid: false,
|
|
user: { email: "user@example.com" },
|
|
delegatedTo: null,
|
|
delegatedToId: null,
|
|
delegationCredentialId: null,
|
|
} as any);
|
|
|
|
const service = new CredentialAccessService();
|
|
await expect(
|
|
service.ensureAccessible({
|
|
credentialId,
|
|
loggedInUserId,
|
|
bookingOwnerId,
|
|
})
|
|
).resolves.not.toThrow();
|
|
});
|
|
});
|