Skip to content

Commit

Permalink
Improve authorization checks and update test for user query resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
nitintwt committed Oct 31, 2024
1 parent 56bcb6f commit a9d4636
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 7 deletions.
21 changes: 16 additions & 5 deletions src/resolvers/Query/user.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { USER_NOT_FOUND_ERROR } from "../../constants";
import { errors } from "../../libraries";
import type { InterfaceAppUserProfile, InterfaceUser } from "../../models";
import { AppUserProfile, User } from "../../models";
import { AppUserProfile, User, Organization } from "../../models";
import type { QueryResolvers } from "../../types/generatedGraphQLTypes";
/**
* This query fetch the user from the database.
Expand All @@ -19,18 +19,29 @@ export const user: QueryResolvers["user"] = async (_parent, args, context) => {
_id: context.userId,
}));

if (currentUserExists === false) {
if (!currentUserExists) {
throw new errors.NotFoundError(
USER_NOT_FOUND_ERROR.DESC,
USER_NOT_FOUND_ERROR.CODE,
USER_NOT_FOUND_ERROR.PARAM,
);
}

// Check if the requesting user is trying to access their own data
if (context.userId !== args.id) {
// Check if the requesting user is an admin of the organization the target user belongs to
const userOrganization = await Organization.exists({
members: args.id,
admins: context.userId, // Ensure the current user is an admin in the target user's organization
});

// Check if the requesting user is a SuperAdmin
const isSuperAdmin = await AppUserProfile.exists({
userId: context.userId,
isSuperAdmin: true,
});

if (!userOrganization && context.userId !== args.id && !isSuperAdmin) {
throw new errors.UnauthorizedError(
"Access denied. You can only view your own profile.",
"Access denied. Only admins of the organization can view this profile.",
);
}

Expand Down
63 changes: 61 additions & 2 deletions tests/resolvers/Query/userAccess.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import { USER_NOT_FOUND_ERROR, BASE_URL } from "../../../src/constants";
import type mongoose from "mongoose";
import { user as userResolver } from "../../../src/resolvers/Query/user";
import { User } from "../../../src/models";
import { User, Organization, AppUserProfile } from "../../../src/models";
import { connect, disconnect } from "../../helpers/db";
import type { TestUserType } from "../../helpers/userAndOrg";
import { createTestUser } from "../../helpers/userAndOrg";
import { beforeAll, afterAll, describe, it, expect } from "vitest";

let testUser: TestUserType;
let anotherTestUser: TestUserType;
let adminUser: TestUserType;
let superAdminUser: TestUserType;
let MONGOOSE_INSTANCE: typeof mongoose;

beforeAll(async () => {
MONGOOSE_INSTANCE = await connect();
testUser = await createTestUser();
anotherTestUser = await createTestUser();
adminUser = await createTestUser();
superAdminUser = await createTestUser();

// Set up admin and super admin roles
await Organization.create({
members: [anotherTestUser?.id],
admins: [adminUser?.id],
});

await AppUserProfile.create({
userId: superAdminUser?.id,
isSuperAdmin: true,
});
});

afterAll(async () => {
Expand Down Expand Up @@ -60,7 +75,51 @@ describe("user Query", () => {
}
});

// Test case 3: Successful access scenario
// Test case 3: Admin access scenario
it("allows an admin to access another user's data within the same organization", async () => {
const args = {
id: anotherTestUser?.id,
};

const context = {
userId: adminUser?.id,
apiRootURL: BASE_URL,
};

const result = await userResolver?.({}, args, context);

const user = await User.findById(anotherTestUser?._id).lean();

expect(result?.user).toEqual({
...user,
organizationsBlockedBy: [],
image: user?.image ? `${BASE_URL}${user.image}` : null,
});
});

// Test case 4: SuperAdmin access scenario
it("allows a super admin to access any user's data", async () => {
const args = {
id: anotherTestUser?.id,
};

const context = {
userId: superAdminUser?.id,
apiRootURL: BASE_URL,
};

const result = await userResolver?.({}, args, context);

const user = await User.findById(anotherTestUser?._id).lean();

expect(result?.user).toEqual({
...user,
organizationsBlockedBy: [],
image: user?.image ? `${BASE_URL}${user.image}` : null,
});
});

// Test case 5: Successful access to own profile
it("successfully returns user data when accessing own profile", async () => {
const args = {
id: testUser?.id,
Expand Down

0 comments on commit a9d4636

Please sign in to comment.