fix: toggleEnabled handler (#25325)
* fix toggle enabled handler * add tests --------- Co-authored-by: CarinaWolli <[email protected]> Co-authored-by: Keith Williams <[email protected]>
This commit is contained in:
co-authored by
CarinaWolli
Keith Williams
parent
c941192979
commit
a7aabc8b4f
+153
@@ -0,0 +1,153 @@
|
||||
import { describe, it, beforeEach, vi, expect } from "vitest";
|
||||
|
||||
import { DelegationCredentialRepository } from "@calcom/features/delegation-credentials/repositories/DelegationCredentialRepository";
|
||||
|
||||
import { toggleDelegationCredentialEnabled } from "./toggleEnabled.handler";
|
||||
|
||||
// Mock the repository
|
||||
vi.mock("@calcom/features/delegation-credentials/repositories/DelegationCredentialRepository", () => ({
|
||||
DelegationCredentialRepository: {
|
||||
findById: vi.fn(),
|
||||
updateById: vi.fn(),
|
||||
findByIdIncludeSensitiveServiceAccountKey: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock other dependencies
|
||||
vi.mock("@calcom/app-store/delegationCredential", () => ({
|
||||
checkIfSuccessfullyConfiguredInWorkspace: vi.fn().mockResolvedValue(true),
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/emails/integration-email-service", () => ({
|
||||
sendDelegationCredentialDisabledEmail: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./getAffectedMembersForDisable.handler", () => ({
|
||||
getAffectedMembersForDisable: vi.fn().mockResolvedValue([]),
|
||||
}));
|
||||
|
||||
vi.mock("./utils", () => ({
|
||||
ensureNoServiceAccountKey: vi.fn((credential) => credential),
|
||||
}));
|
||||
|
||||
describe("toggleDelegationCredentialEnabled - Security Fix", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should prevent users without organizationId from accessing any credentials", async () => {
|
||||
const userWithoutOrg = {
|
||||
id: 1,
|
||||
email: "[email protected]",
|
||||
organizationId: null,
|
||||
};
|
||||
|
||||
const input = {
|
||||
id: "any-credential",
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
const mockCredential = {
|
||||
id: "any-credential",
|
||||
organizationId: 1,
|
||||
enabled: false,
|
||||
workspacePlatform: { slug: "google" },
|
||||
};
|
||||
|
||||
vi.mocked(DelegationCredentialRepository.findById).mockResolvedValue(mockCredential);
|
||||
|
||||
await expect(
|
||||
toggleDelegationCredentialEnabled(userWithoutOrg, input)
|
||||
).rejects.toThrow("You must be part of an organization to toggle a delegation credential");
|
||||
|
||||
expect(DelegationCredentialRepository.updateById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should prevent cross-organization access", async () => {
|
||||
const userFromOrg1 = {
|
||||
id: 1,
|
||||
email: "[email protected]",
|
||||
organizationId: 1,
|
||||
};
|
||||
|
||||
const input = {
|
||||
id: "org2-credential",
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
const org2Credential = {
|
||||
id: "org2-credential",
|
||||
organizationId: 2, // Different organization
|
||||
enabled: true,
|
||||
workspacePlatform: { slug: "google" },
|
||||
};
|
||||
|
||||
vi.mocked(DelegationCredentialRepository.findById).mockResolvedValue(org2Credential);
|
||||
|
||||
await expect(
|
||||
toggleDelegationCredentialEnabled(userFromOrg1, input)
|
||||
).rejects.toThrow("Delegation credential not found");
|
||||
|
||||
expect(DelegationCredentialRepository.updateById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should allow same-organization access", async () => {
|
||||
const userFromOrg1 = {
|
||||
id: 1,
|
||||
email: "[email protected]",
|
||||
organizationId: 1,
|
||||
};
|
||||
|
||||
const input = {
|
||||
id: "org1-credential",
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
const org1Credential = {
|
||||
id: "org1-credential",
|
||||
organizationId: 1, // Same organization
|
||||
enabled: true,
|
||||
workspacePlatform: { slug: "google" },
|
||||
};
|
||||
|
||||
const updatedCredential = {
|
||||
...org1Credential,
|
||||
enabled: false,
|
||||
lastDisabledAt: new Date(),
|
||||
};
|
||||
|
||||
vi.mocked(DelegationCredentialRepository.findById).mockResolvedValue(org1Credential);
|
||||
vi.mocked(DelegationCredentialRepository.updateById).mockResolvedValue(updatedCredential);
|
||||
|
||||
const result = await toggleDelegationCredentialEnabled(userFromOrg1, input);
|
||||
|
||||
expect(result).toEqual(updatedCredential);
|
||||
expect(DelegationCredentialRepository.updateById).toHaveBeenCalledWith({
|
||||
id: "org1-credential",
|
||||
data: {
|
||||
enabled: false,
|
||||
lastEnabledAt: undefined,
|
||||
lastDisabledAt: expect.any(Date),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle nonexistent credentials", async () => {
|
||||
const user = {
|
||||
id: 1,
|
||||
email: "[email protected]",
|
||||
organizationId: 1,
|
||||
};
|
||||
|
||||
const input = {
|
||||
id: "nonexistent-credential",
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
vi.mocked(DelegationCredentialRepository.findById).mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
toggleDelegationCredentialEnabled(user, input)
|
||||
).rejects.toThrow("Delegation credential not found");
|
||||
});
|
||||
});
|
||||
@@ -53,6 +53,14 @@ export async function toggleDelegationCredentialEnabled(
|
||||
throw new Error("Delegation credential not found");
|
||||
}
|
||||
|
||||
if (!loggedInUser.organizationId) {
|
||||
throw new Error("You must be part of an organization to toggle a delegation credential");
|
||||
}
|
||||
|
||||
if (currentDelegationCredential.organizationId !== loggedInUser.organizationId) {
|
||||
throw new Error("Delegation credential not found");
|
||||
}
|
||||
|
||||
const shouldBeEnabled = input.enabled;
|
||||
|
||||
if (shouldBeEnabled === currentDelegationCredential.enabled) {
|
||||
|
||||
Reference in New Issue
Block a user