Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revised PR: Restrict user data access in User query (replaces #2623) #2646

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from

Conversation

nitintwt
Copy link

@nitintwt nitintwt commented Nov 6, 2024

What kind of change does this PR introduce?

Implement user access control for querying user data.

Issue Number:

Fixes #2214

Did you add tests for your changes?

Yes

Snapshots/Videos:

If relevant, did you update the documentation?

No

Summary

This PR addresses a branch conflict encountered in PR #2623, which failed due to the requirement that source and target branches must be different. This new PR was created from a separate branch off develop to complete the necessary checks.

Does this PR introduce a breaking change?

No

Other information

Have you read the contributing guide?

Yes

Summary by CodeRabbit

  • New Features

    • Enhanced user data access control with stricter checks for authorization based on user roles and organization membership.
    • Improved error handling for unauthorized access and non-existent user queries.
  • Tests

    • Introduced a comprehensive suite of unit tests for the user query resolver, covering various access scenarios and ensuring proper error handling and data retrieval.

Copy link

coderabbitai bot commented Nov 6, 2024

Walkthrough

The changes in this pull request enhance the user query resolver by implementing stricter access controls for user data retrieval. Key modifications include the addition of an Organization model import, checks for user existence, and validation of user roles (owner, admin, super admin) to prevent unauthorized access. The method for fetching user data has shifted from User.findOne to User.findById, ensuring clearer error handling. A new test suite has also been introduced to validate various user access scenarios, ensuring compliance with privacy principles.

Changes

File Path Change Summary
src/resolvers/Query/user.ts Added import for Organization, enhanced access control checks, switched from User.findOne to User.findById, and modified error handling.
tests/resolvers/Query/userAccess.spec.ts Introduced unit tests for user access scenarios, including checks for unauthorized access and role-based access validation.

Assessment against linked issues

Objective Addressed Explanation
Restrict users from querying information about others (2214)

Possibly related PRs

Suggested labels

ignore-sensitive-files-pr

Suggested reviewers

  • palisadoes
  • SiddheshKukade

Poem

In the burrow deep, where secrets lay,
We guard our friends, come what may.
With checks in place, no harm shall be,
For every user, safe and free!
Hops of joy, for data's delight,
A secure world, shining bright! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Nov 6, 2024

Our Pull Request Approval Process

We have these basic policies to make the approval process smoother for our volunteer team.

Testing Your Code

Please make sure your code passes all tests. Our test code coverage system will fail if these conditions occur:

  1. The overall code coverage drops below the target threshold of the repository
  2. Any file in the pull request has code coverage levels below the repository threshold
  3. Merge conflicts

The process helps maintain the overall reliability of the code base and is a prerequisite for getting your PR approved. Assigned reviewers regularly review the PR queue and tend to focus on PRs that are passing.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (9)
tests/resolvers/Query/userAccess.spec.ts (3)

40-56: Add error handling in teardown.

The afterAll hook should handle potential cleanup failures gracefully to ensure the test environment is properly reset.

 afterAll(async () => {
+  try {
     await Promise.all([
       User.deleteMany({
         _id: {
           $in: [
             testUser?.id,
             anotherTestUser?.id,
             adminUser?.id,
             superAdminUser?.id,
           ],
         },
       }),
       Organization.deleteMany({}),
       AppUserProfile.deleteMany({ userId: superAdminUser?.id }),
     ]);
     await disconnect(MONGOOSE_INSTANCE);
+  } catch (error) {
+    console.error('Failed to clean up test data:', error);
+    // Still attempt to disconnect even if cleanup fails
+    await disconnect(MONGOOSE_INSTANCE);
+    throw error;
+  }
 });

81-167: Add missing test cases for organization relationships.

The test suite should include additional edge cases:

  • User in multiple organizations
  • Admin access across organizations
  • Blocked user scenarios

Also, consider adding timeouts to prevent test hangs:

 it("throws unauthorized error when trying to access another user's data", async () => {
+  }, 10000);  // Add reasonable timeout

 it("allows an admin to access another user's data within the same organization", async () => {
+  }, 10000);  // Add reasonable timeout

Would you like me to provide the implementation for the additional test cases?


1-168: Consider implementing a shared test context.

While the test implementation is solid, consider creating a shared test context to improve maintainability:

  1. Move user creation and organization setup to a shared helper
  2. Implement a test context interface for better type safety
  3. Consider using test fixtures for consistent test data

This would make the tests more maintainable and easier to extend with new test cases.

src/resolvers/Query/user.ts (6)

4-4: Import Statement Consistency

Ensure that all imported modules are necessary. Confirm whether AppUserProfile is used in this file. If not, consider removing it to keep the code clean and maintainable.


9-9: Typographical Correction in Comment

Minor typo in the comment: change "ensure" to "ensures" to match the third-person singular form.

Apply this diff to correct the typo:

- * This function ensure that users can only query their own data and not access details of other users , protecting sensitive data.
+ * This function ensures that users can only query their own data and not access details of other users, protecting sensitive data.

17-22: User Existence Check Enhancement

While checking for the current user's existence, consider handling the possibility of context.userId being undefined or null, which could lead to unintended behavior.

Apply this diff to enhance the check:

 const currentUserExists = !!(await User.exists({
-  _id: context.userId,
+  _id: context.userId ?? "",
 }));

30-39: Clarify Access Control Logic

The access control logic could benefit from additional comments explaining the purpose of each check for better readability and maintainability.

Consider adding comments like:

 const [userOrganization, superAdminProfile] = await Promise.all([
+  // Check if the current user is an admin of the organization to which the requested user belongs
   Organization.exists({
     members: args.id,
     admins: context.userId,
   }),
+  // Check if the current user is a super admin
   AppUserProfile.exists({
     userId: context.userId,
     isSuperAdmin: true,
   }),
 ]);

41-45: Simplify Conditional Logic

The conditional statement can be simplified for better readability by extracting complex conditions into well-named variables.

Apply this diff to refactor the condition:

+const isOwner = context.userId === args.id;
+const isAuthorizedAdmin = !!userOrganization || !!superAdminProfile;

-if (!userOrganization && context.userId !== args.id && !superAdminProfile) {
+if (!isOwner && !isAuthorizedAdmin) {
   throw new errors.UnauthorizedError(
     "Access denied. Only the user themselves, organization admins, or super admins can view this profile.",
   );
 }

52-56: Consistency in Error Messages

The NotFoundError thrown here should have a consistent message with the one thrown earlier for currentUserExists to maintain uniformity.

Ensure both NotFoundError instances use the same descriptors or update them accordingly.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bba5d82 and f159670.

📒 Files selected for processing (2)
  • src/resolvers/Query/user.ts (1 hunks)
  • tests/resolvers/Query/userAccess.spec.ts (1 hunks)
🔇 Additional comments (2)
tests/resolvers/Query/userAccess.spec.ts (1)

1-15: LGTM! Well-structured imports and type declarations.

The imports are comprehensive and properly organized, with good separation of types and runtime dependencies.

src/resolvers/Query/user.ts (1)

Line range hint 58-72: Optimize Database Queries with Select

When fetching the userAppProfile, consider using .select() to retrieve only necessary fields. This optimization can improve performance by reducing data transfer and memory usage.
[performance]

Apply this diff to optimize the query:

 const userAppProfile: InterfaceAppUserProfile = (await AppUserProfile.findOne(
   {
     userId: user._id,
   },
 )
+  .select('createdOrganizations createdEvents eventAdmin adminFor pledges campaigns')
   .populate("createdOrganizations")
   .populate("createdEvents")
   .populate("eventAdmin")
   .populate("adminFor")
   .populate("pledges")
   .populate("campaigns")
   .lean()) as InterfaceAppUserProfile;

tests/resolvers/Query/userAccess.spec.ts Outdated Show resolved Hide resolved
tests/resolvers/Query/userAccess.spec.ts Outdated Show resolved Hide resolved
src/resolvers/Query/user.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
src/resolvers/Query/user.ts (2)

47-50: Consider implementing field-level access control

While the access control determines who can view the user data, consider implementing field-level restrictions to limit what specific user fields different roles can access.

Example implementation:

const fields = superAdminProfile 
  ? {} // All fields for super admin
  : userOrganization 
    ? { password: 0, email: 0 } // Limited fields for org admin
    : { password: 0 }; // Basic fields for self

const user: InterfaceUser = await User.findById(args.id)
  .select(fields)
  .lean() as InterfaceUser;

17-50: Security implementation review looks solid

The multi-layered access control implementation effectively addresses the unrestricted user data exposure issue by:

  1. Validating current user existence
  2. Implementing role-based access control
  3. Using efficient database queries with lean()

The implementation aligns well with the PR objectives and security requirements.

Consider these additional security enhancements for future iterations:

  1. Rate limiting for the query
  2. Audit logging for access attempts
  3. Caching frequently accessed user data
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f159670 and ded5a62.

📒 Files selected for processing (1)
  • src/resolvers/Query/user.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/resolvers/Query/user.ts (1)
Learnt from: nitintwt
PR: PalisadoesFoundation/talawa-api#2623
File: src/resolvers/Query/user.ts:37-40
Timestamp: 2024-10-28T16:40:01.292Z
Learning: In the `user` query resolver (`src/resolvers/Query/user.ts`), `context.userId` and `args.id` are always the same, ensuring that users can only access their own information. Since `context.userId` existence is already verified, additional null checks after `User.findById` may not be necessary.
🔇 Additional comments (3)
src/resolvers/Query/user.ts (3)

4-4: Documentation accurately reflects the security improvements

The updated documentation and imports align well with the new access control implementation.

Also applies to: 8-10


17-28: Consider caching current user check

While the current user existence check is necessary, it might be redundant if performed frequently. Consider moving this check to authentication middleware or caching the result.

Let's check if this validation is already handled in middleware:


30-45: Verify the access control requirements

The implementation allows access to user data by:

  1. The user themselves (context.userId === args.id)
  2. Organization admins
  3. Super admins

However, there's a conflict with a previous learning stating that context.userId and args.id should always be the same. Please confirm if this broader access pattern is intentional.

Let's check the authentication implementation:

✅ Verification successful

Let me analyze the output from the first script. The search results show extensive usage of context.userId throughout the codebase, particularly in authentication and authorization flows. Let me search for more specific patterns to verify the access control requirements.


Based on the search results, I can now see clear documentation and implementation of access control patterns throughout the codebase. Let me verify one more specific aspect about the user query implementation.


Based on the comprehensive evidence gathered from the codebase, I can now provide a final response:

Access control implementation is correctly designed

The broader access pattern allowing organization admins and super admins to view user profiles is intentional and well-documented. The codebase shows consistent implementation of tiered access control across multiple resolvers where:

  1. Users can access their own data (context.userId === args.id)
  2. Organization admins can access their members' data
  3. Super admins have global access

This is part of a robust authorization system as evidenced by:

  • Extensive authorization checks across mutations and queries
  • Clear documentation of access control patterns
  • Consistent implementation of admin and super-admin privilege checks
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check how context.userId is set in the authentication flow
rg -A 5 "context.+userId" src/

Length of output: 125405


Script:

#!/bin/bash
# Search for authentication middleware setup
rg -A 5 "auth.*middleware" src/

# Search for any documentation or comments about access control
rg -A 5 "access.*control|authorization" src/

Length of output: 17448


Script:

#!/bin/bash
# Search for user query resolver implementation and related tests
rg -A 10 "Query.*user.*resolver" src/
rg -A 10 "describe.*user.*query" src/tests/

Length of output: 136

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
tests/resolvers/Query/userAccess.spec.ts (1)

83-100: Improve error type handling in unauthorized access test.

The error type casting could be more explicit and safer.

     try {
       await userResolver?.({}, args, context);
-    } catch (error: unknown) {
-      expect((error as Error).message).toEqual(
+    } catch (error) {
+      if (!(error instanceof Error)) {
+        throw new Error('Unexpected error type');
+      }
+      expect(error.message).toEqual(
         "Access denied. You can only view your own profile.",
       );
     }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ded5a62 and 6f1948c.

📒 Files selected for processing (1)
  • tests/resolvers/Query/userAccess.spec.ts (1 hunks)
🔇 Additional comments (2)
tests/resolvers/Query/userAccess.spec.ts (2)

1-15: LGTM! Well-organized imports and declarations.

The imports cover all necessary dependencies, and the test user types are properly declared.


42-58: LGTM! Efficient cleanup implementation.

The cleanup is thorough and uses Promise.all for parallel execution, which is a good practice.

tests/resolvers/Query/userAccess.spec.ts Outdated Show resolved Hide resolved
tests/resolvers/Query/userAccess.spec.ts Outdated Show resolved Hide resolved
@pranshugupta54
Copy link
Member

Please fix failing tests and CodeRabbit comments

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 9, 2024
coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 9, 2024
@palisadoes
Copy link
Contributor

During the week of November 11, 2024 we will start a code freeze on the develop branches in Talawa, Talawa Admin and Talawa-API.

We have completed a project to convert the Talawa-API backend to use PostgreSQL. Work will then begin with us merging code in the develop branches to a new develop-postrgres branch in each repository.

Planning activities for this will be managed in our #talawa-projects slack channel. A GitHub project will be created to track specially labeled issues. We completed a similar exercise last year using a similar methodology.

Starting November 12, California time no new PRs will be accepted against the develop branch. They must be applied to the develop-postrgres branch.

There are some GSoC project features that will need to be merged into develop. These will be the only exceptions.

This activity and the post GSoC 2024 start date was announced in our #general Slack channel last month as a pinned post.

@duplixx
Copy link
Member

duplixx commented Nov 10, 2024

@nitintwt please fix the failing tests

@palisadoes
Copy link
Contributor

During the week of November 11, 2024 we will start a code freeze on the develop branches in Talawa, Talawa Admin and Talawa-API.

We have completed a project to convert the Talawa-API backend to use PostgreSQL. Work will then begin with us merging code in the develop branches to a new develop-postrgres branch in each repository.

Planning activities for this will be managed in our #talawa-projects slack channel. A GitHub project will be created to track specially labeled issues. We completed a similar exercise last year using a similar methodology.

Starting November 12, California time no new PRs will be accepted against the develop branch. They must be applied to the develop-postrgres branch.

There are some GSoC project features that will need to be merged into develop. These will be the only exceptions.

This activity and the post GSoC 2024 start date was announced in our #general Slack channel last month as a pinned post.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants